summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorTon Voon <tonvoon@users.sourceforge.net>2008-03-19 14:42:12 (GMT)
committerTon Voon <tonvoon@users.sourceforge.net>2008-03-19 14:42:12 (GMT)
commitb17b2421987bb8a7606948333e75f990b35852b8 (patch)
tree5d220f355728f1448f9dbd6f796631bbfef0733c /lib
parentcab9440a671390e279228cd31ad56b055d611a21 (diff)
downloadmonitoring-plugins-b17b2421987bb8a7606948333e75f990b35852b8.tar.gz
1st pass at check_procs with multiple threshold checks
git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/nagiosplug/branches/new_threshold_syntax@1958 f882894a-f735-0410-b71e-b25c423dba1c
Diffstat (limited to 'lib')
-rw-r--r--lib/tests/test_utils.c158
-rw-r--r--lib/utils_base.c135
-rw-r--r--lib/utils_base.h21
3 files changed, 306 insertions, 8 deletions
diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c
index 278f526..a5189ce 100644
--- a/lib/tests/test_utils.c
+++ b/lib/tests/test_utils.c
@@ -30,7 +30,7 @@ main (int argc, char **argv)
30 thresholds *thresholds = NULL; 30 thresholds *thresholds = NULL;
31 int rc; 31 int rc;
32 32
33 plan_tests(82); 33 plan_tests(172);
34 34
35 range = parse_range_string("6"); 35 range = parse_range_string("6");
36 ok( range != NULL, "'6' is valid range"); 36 ok( range != NULL, "'6' is valid range");
@@ -40,6 +40,32 @@ main (int argc, char **argv)
40 ok( range->end_infinity == FALSE, "Not using infinity"); 40 ok( range->end_infinity == FALSE, "Not using infinity");
41 free(range); 41 free(range);
42 42
43 range = _parse_range_string_v2("6");
44 ok( range == NULL, "Missing colon in range" );
45 ok( utils_errno == NP_RANGE_MISSING_COLON, "Right error code" );
46
47 range = _parse_range_string_v2("6:");
48 ok( range != NULL, "'6:' is valid range");
49 ok( range->start == 6, "Start correct");
50 ok( range->start_infinity == FALSE, "Not using negative infinity");
51 ok( range->end_infinity == TRUE, "Using infinity");
52 free(range);
53
54 range = _parse_range_string_v2("6:6");
55 ok( range != NULL, "'6:6' is valid range");
56 ok( range->start == 6, "Start correct");
57 ok( range->start_infinity == FALSE, "Not using negative infinity");
58 ok( range->end == 6, "End correct");
59 ok( range->end_infinity == FALSE, "Not using infinity");
60 free(range);
61
62 range = _parse_range_string_v2(":6");
63 ok( range != NULL, "':6' is valid range");
64 ok( range->start_infinity == TRUE, "Using negative infinity");
65 ok( range->end == 6, "End correct");
66 ok( range->end_infinity == FALSE, "Not using infinity");
67 free(range);
68
43 range = parse_range_string("1:12%%"); 69 range = parse_range_string("1:12%%");
44 ok( range != NULL, "'1:12%%' is valid - percentages are ignored"); 70 ok( range != NULL, "'1:12%%' is valid - percentages are ignored");
45 ok( range->start == 1, "Start correct"); 71 ok( range->start == 1, "Start correct");
@@ -48,6 +74,14 @@ main (int argc, char **argv)
48 ok( range->end_infinity == FALSE, "Not using infinity"); 74 ok( range->end_infinity == FALSE, "Not using infinity");
49 free(range); 75 free(range);
50 76
77 range = _parse_range_string_v2("1:12%%");
78 ok( range != NULL, "'1:12%%' is valid - percentages are ignored");
79 ok( range->start == 1, "Start correct");
80 ok( range->start_infinity == FALSE, "Not using negative infinity");
81 ok( range->end == 12, "End correct");
82 ok( range->end_infinity == FALSE, "Not using infinity");
83 free(range);
84
51 range = parse_range_string("-7:23"); 85 range = parse_range_string("-7:23");
52 ok( range != NULL, "'-7:23' is valid range"); 86 ok( range != NULL, "'-7:23' is valid range");
53 ok( range->start == -7, "Start correct"); 87 ok( range->start == -7, "Start correct");
@@ -56,6 +90,14 @@ main (int argc, char **argv)
56 ok( range->end_infinity == FALSE, "Not using infinity"); 90 ok( range->end_infinity == FALSE, "Not using infinity");
57 free(range); 91 free(range);
58 92
93 range = _parse_range_string_v2("-7:23");
94 ok( range != NULL, "'-7:23' is valid range");
95 ok( range->start == -7, "Start correct");
96 ok( range->start_infinity == FALSE, "Not using negative infinity");
97 ok( range->end == 23, "End correct");
98 ok( range->end_infinity == FALSE, "Not using infinity");
99 free(range);
100
59 range = parse_range_string(":5.75"); 101 range = parse_range_string(":5.75");
60 ok( range != NULL, "':5.75' is valid range"); 102 ok( range != NULL, "':5.75' is valid range");
61 ok( range->start == 0, "Start correct"); 103 ok( range->start == 0, "Start correct");
@@ -64,6 +106,14 @@ main (int argc, char **argv)
64 ok( range->end_infinity == FALSE, "Not using infinity"); 106 ok( range->end_infinity == FALSE, "Not using infinity");
65 free(range); 107 free(range);
66 108
109 range = _parse_range_string_v2(":5.75");
110 ok( range != NULL, "':5.75' is valid range");
111 ok( range->start == 0, "Start correct");
112 ok( range->start_infinity == TRUE, "Using negative infinity");
113 ok( range->end == 5.75, "End correct");
114 ok( range->end_infinity == FALSE, "Not using infinity");
115 free(range);
116
67 range = parse_range_string("~:-95.99"); 117 range = parse_range_string("~:-95.99");
68 ok( range != NULL, "~:-95.99' is valid range"); 118 ok( range != NULL, "~:-95.99' is valid range");
69 ok( range->start_infinity == TRUE, "Using negative infinity"); 119 ok( range->start_infinity == TRUE, "Using negative infinity");
@@ -71,6 +121,26 @@ main (int argc, char **argv)
71 ok( range->end_infinity == FALSE, "Not using infinity"); 121 ok( range->end_infinity == FALSE, "Not using infinity");
72 free(range); 122 free(range);
73 123
124 range = _parse_range_string_v2("~:-95.99");
125 ok( range == NULL, "~:-95.99' is invalid range");
126 ok( utils_errno == NP_RANGE_UNPARSEABLE, "Correct error code" );
127
128 /*
129 * This is currently parseable. This is because ~ is interpreted as a 0
130 * and then 95.99 is the end, so we get 0:95.99. Should validate the characters before
131 * passing to strtod
132 range = _parse_range_string_v2("~:95.99");
133 ok( range == NULL, "~:95.99' is invalid range");
134 ok( utils_errno == NP_RANGE_UNPARSEABLE, "Correct error code" );
135 */
136
137 range = _parse_range_string_v2(":-95.99");
138 ok( range != NULL, ":-95.99' is valid range");
139 ok( range->start_infinity == TRUE, "Using negative infinity");
140 ok( range->end == -95.99, "End correct (with rounding errors)");
141 ok( range->end_infinity == FALSE, "Not using infinity");
142 free(range);
143
74 range = parse_range_string("12345678901234567890:"); 144 range = parse_range_string("12345678901234567890:");
75 temp = atof("12345678901234567890"); /* Can't just use this because number too large */ 145 temp = atof("12345678901234567890"); /* Can't just use this because number too large */
76 ok( range != NULL, "'12345678901234567890:' is valid range"); 146 ok( range != NULL, "'12345678901234567890:' is valid range");
@@ -83,6 +153,14 @@ main (int argc, char **argv)
83 ok( check_range(temp*2, range) == FALSE, "12345678901234567890*2 - no alert"); 153 ok( check_range(temp*2, range) == FALSE, "12345678901234567890*2 - no alert");
84 free(range); 154 free(range);
85 155
156 range = _parse_range_string_v2("12345678901234567890:");
157 temp = atof("12345678901234567890");
158 ok( range != NULL, "'12345678901234567890:' is valid range");
159 ok( range->start == temp, "Start correct");
160 ok( range->start_infinity == FALSE, "Not using negative infinity");
161 ok( range->end_infinity == TRUE, "Using infinity");
162 free(range);
163
86 range = parse_range_string("~:0"); 164 range = parse_range_string("~:0");
87 ok( range != NULL, "'~:0' is valid range"); 165 ok( range != NULL, "'~:0' is valid range");
88 ok( range->start_infinity == TRUE, "Using negative infinity"); 166 ok( range->start_infinity == TRUE, "Using negative infinity");
@@ -94,8 +172,16 @@ main (int argc, char **argv)
94 ok( check_range(0, range) == FALSE, "0 - no alert"); 172 ok( check_range(0, range) == FALSE, "0 - no alert");
95 free(range); 173 free(range);
96 174
175 range = _parse_range_string_v2("-4.33:-4.33");
176 ok( range != NULL, "'-4.33:-4.33' is valid range");
177 ok( range->start_infinity == FALSE, "Not using negative infinity");
178 ok( range->start == -4.33, "Start right");
179 ok( range->end == -4.33, "End correct");
180 ok( range->end_infinity == FALSE, "Not using infinity");
181 free(range);
182
97 range = parse_range_string("@0:657.8210567"); 183 range = parse_range_string("@0:657.8210567");
98 ok( range != 0, "@0:657.8210567' is a valid range"); 184 ok( range != NULL, "@0:657.8210567' is a valid range");
99 ok( range->start == 0, "Start correct"); 185 ok( range->start == 0, "Start correct");
100 ok( range->start_infinity == FALSE, "Not using negative infinity"); 186 ok( range->start_infinity == FALSE, "Not using negative infinity");
101 ok( range->end == 657.8210567, "End correct"); 187 ok( range->end == 657.8210567, "End correct");
@@ -107,6 +193,14 @@ main (int argc, char **argv)
107 ok( check_range(0, range) == TRUE, "0 - alert"); 193 ok( check_range(0, range) == TRUE, "0 - alert");
108 free(range); 194 free(range);
109 195
196 range = parse_range_string("^0:657.8210567");
197 ok( range != NULL, "^0:657.8210567' is a valid range");
198 ok( range->start == 0, "Start correct");
199 ok( range->start_infinity == FALSE, "Not using negative infinity");
200 ok( range->end == 657.8210567, "End correct");
201 ok( range->end_infinity == FALSE, "Not using infinity");
202 free(range);
203
110 range = parse_range_string("1:1"); 204 range = parse_range_string("1:1");
111 ok( range != NULL, "'1:1' is a valid range"); 205 ok( range != NULL, "'1:1' is a valid range");
112 ok( range->start == 1, "Start correct"); 206 ok( range->start == 1, "Start correct");
@@ -121,22 +215,71 @@ main (int argc, char **argv)
121 range = parse_range_string("2:1"); 215 range = parse_range_string("2:1");
122 ok( range == NULL, "'2:1' rejected"); 216 ok( range == NULL, "'2:1' rejected");
123 217
218 range = _parse_range_string_v2("2:1");
219 ok( range == NULL, "'2:1' rejected");
220 ok( utils_errno == NP_RANGE_UNPARSEABLE, "Errno correct" );
221
124 rc = _set_thresholds(&thresholds, NULL, NULL); 222 rc = _set_thresholds(&thresholds, NULL, NULL);
125 ok( rc == 0, "Thresholds (NULL, NULL) set"); 223 ok( rc == 0, "Thresholds (NULL, NULL) set");
126 ok( thresholds->warning == NULL, "Warning not set"); 224 ok( thresholds->warning == NULL, "Warning not set");
127 ok( thresholds->critical == NULL, "Critical not set"); 225 ok( thresholds->critical == NULL, "Critical not set");
128 226
227 thresholds = _parse_thresholds_string(NULL);
228 ok( thresholds != NULL, "Threshold still set, even though NULL");
229 ok( thresholds->warning == NULL, "Warning set to NULL");
230 ok( thresholds->critical == NULL, "Critical set to NULL");
231
232 thresholds = _parse_thresholds_string("");
233 ok( thresholds != NULL, "Threshold still set, even though ''");
234 ok( thresholds->warning == NULL, "Warning set to NULL");
235 ok( thresholds->critical == NULL, "Critical set to NULL");
236
237 thresholds = _parse_thresholds_string("/");
238 ok( thresholds != NULL, "Threshold still set, even though '/'");
239 ok( thresholds->warning == NULL, "Warning set to NULL");
240 ok( thresholds->critical == NULL, "Critical set to NULL");
241
129 rc = _set_thresholds(&thresholds, NULL, "80"); 242 rc = _set_thresholds(&thresholds, NULL, "80");
130 ok( rc == 0, "Thresholds (NULL, '80') set"); 243 ok( rc == 0, "Thresholds (NULL, '80') set");
131 ok( thresholds->warning == NULL, "Warning not set"); 244 ok( thresholds->warning == NULL, "Warning not set");
132 ok( thresholds->critical->end == 80, "Critical set correctly"); 245 ok( thresholds->critical->end == 80, "Critical set correctly");
133 246
247 thresholds = _parse_thresholds_string(":80/");
248 ok( thresholds != NULL, "Threshold set for ':80/'");
249 ok( thresholds->warning == NULL, "Warning set to NULL");
250 ok( thresholds->critical->start_infinity == TRUE, "Start is right" );
251 ok( thresholds->critical->end == 80, "Critical set to 80");
252
253 thresholds = _parse_thresholds_string(":80");
254 ok( thresholds != NULL, "Threshold set for ':80'");
255 ok( thresholds->warning == NULL, "Warning set to NULL");
256 ok( thresholds->critical->start_infinity == TRUE, "Start is right" );
257 ok( thresholds->critical->end == 80, "Critical set to 80");
258
259 thresholds = _parse_thresholds_string("80");
260 ok( thresholds == NULL, "Threshold not set because of single value '80'");
261 ok( utils_errno == NP_RANGE_MISSING_COLON, "Correct error message");
262
134 rc = _set_thresholds(&thresholds, "5:33", NULL); 263 rc = _set_thresholds(&thresholds, "5:33", NULL);
135 ok( rc == 0, "Thresholds ('5:33', NULL) set"); 264 ok( rc == 0, "Thresholds ('5:33', NULL) set");
136 ok( thresholds->warning->start == 5, "Warning start set"); 265 ok( thresholds->warning->start == 5, "Warning start set");
137 ok( thresholds->warning->end == 33, "Warning end set"); 266 ok( thresholds->warning->end == 33, "Warning end set");
138 ok( thresholds->critical == NULL, "Critical not set"); 267 ok( thresholds->critical == NULL, "Critical not set");
139 268
269 thresholds = _parse_thresholds_string("5:33");
270 ok( thresholds != NULL, "Threshold set for '5:33'");
271 ok( thresholds->warning == NULL, "Warning set to NULL");
272 ok( thresholds->critical->start_infinity == FALSE, "Start is right" );
273 ok( thresholds->critical->start == 5, "Critical set to 5");
274 ok( thresholds->critical->end == 33, "Critical set to 33");
275
276 thresholds = _parse_thresholds_string("/5:33");
277 ok( thresholds != NULL, "Threshold set for '/5:33'");
278 ok( thresholds->critical == NULL, "Critical set to NULL");
279 ok( thresholds->warning->start_infinity == FALSE, "Start is right" );
280 ok( thresholds->warning->start == 5, "Warning start set to 5");
281 ok( thresholds->warning->end == 33, "Warning end set to 33");
282
140 rc = _set_thresholds(&thresholds, "30", "60"); 283 rc = _set_thresholds(&thresholds, "30", "60");
141 ok( rc == 0, "Thresholds ('30', '60') set"); 284 ok( rc == 0, "Thresholds ('30', '60') set");
142 ok( thresholds->warning->end == 30, "Warning set correctly"); 285 ok( thresholds->warning->end == 30, "Warning set correctly");
@@ -145,6 +288,17 @@ main (int argc, char **argv)
145 ok( get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); 288 ok( get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning");
146 ok( get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); 289 ok( get_status(69, thresholds) == STATE_CRITICAL, "69 - critical");
147 290
291 thresholds = _parse_thresholds_string("-6.7:29 / 235.4:3333.33");
292 ok( thresholds != NULL, "Threshold set for '-6.7:29 / 235.4:3333.33'");
293 ok( thresholds->critical->start_infinity == FALSE, "Critical not starting at infinity");
294 ok( thresholds->critical->start == -6.7, "Critical start right" );
295 ok( thresholds->critical->end_infinity == FALSE, "Critical not ending at infinity");
296 ok( thresholds->critical->end == 29, "Critical end right" );
297 ok( thresholds->warning->start_infinity == FALSE, "Start is right" );
298 ok( thresholds->warning->start == 235.4, "Warning set to 5");
299 ok( thresholds->warning->end_infinity == FALSE, "End is right" );
300 ok( thresholds->warning->end == 3333.33, "Warning set to 33");
301
148 char *test; 302 char *test;
149 test = np_escaped_string("bob\\n"); 303 test = np_escaped_string("bob\\n");
150 ok( strcmp(test, "bob\n") == 0, "bob\\n ok"); 304 ok( strcmp(test, "bob\n") == 0, "bob\\n ok");
diff --git a/lib/utils_base.c b/lib/utils_base.c
index d1453c6..1320b71 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -98,6 +98,138 @@ range
98 return NULL; 98 return NULL;
99} 99}
100 100
101/* New range string parsing routines, for the v2 thresholds syntax */
102/* Returns range if OK, otherwise errors. Sets utils_errno if error */
103int utils_errno;
104range *
105_parse_range_string_v2 (char *str) {
106 range *temp_range;
107 double start;
108 double end;
109 char *end_str;
110
111 temp_range = (range *) malloc(sizeof(range));
112
113 /* Initialise */
114 temp_range->start = 0;
115 temp_range->start_infinity = FALSE;
116 temp_range->end = 0;
117 temp_range->end_infinity = FALSE;
118 temp_range->alert_on = INSIDE;
119
120 if (str[0] == '^') {
121 temp_range->alert_on = OUTSIDE;
122 str++;
123 }
124
125 end_str = index(str, ':');
126 if (end_str == NULL) {
127 utils_errno = NP_RANGE_MISSING_COLON;
128 free(temp_range);
129 temp_range = NULL;
130 return NULL;
131 }
132 end_str++;
133 if (str[0] == ':') {
134 temp_range->start_infinity = TRUE;
135 } else {
136 start = strtod(str, NULL); /* Will stop at ':' */
137 set_range_start(temp_range, start);
138 }
139 if (strcmp(end_str, "") != 0) {
140 end = strtod(end_str, NULL);
141 set_range_end(temp_range, end);
142 } else {
143 temp_range->end_infinity = TRUE;
144 }
145
146 if (temp_range->start_infinity == TRUE ||
147 temp_range->end_infinity == TRUE ||
148 temp_range->start <= temp_range->end) {
149 return temp_range;
150 }
151 free(temp_range);
152 temp_range = NULL;
153 utils_errno = NP_RANGE_UNPARSEABLE;
154 return NULL;
155}
156
157void
158_die_on_parse_error(int errorcode) {
159 switch (errorcode) {
160 case NP_RANGE_UNPARSEABLE:
161 die(STATE_UNKNOWN, _("Range format incorrect\n"));
162 case NP_WARN_WITHIN_CRIT:
163 die(STATE_UNKNOWN, _("Warning level is a subset of critical and will not be alerted\n"));
164 case NP_RANGE_MISSING_COLON:
165 die(STATE_UNKNOWN, _("Range is missing a colon\n"));
166 case NP_MEMORY_ERROR:
167 die(STATE_UNKNOWN, _("Memory error\n"));
168 }
169}
170
171thresholds
172*_parse_thresholds_string(char *string) {
173 thresholds *temp_thresholds = NULL;
174 char *separator = NULL;
175 char *temp_string = NULL;
176 range *temp_range = NULL;
177 int rc;
178
179 temp_thresholds = malloc(sizeof(temp_thresholds));
180 if (temp_thresholds == NULL) {
181 utils_errno = NP_MEMORY_ERROR;
182 return NULL;
183 }
184
185 temp_thresholds->warning = NULL;
186 temp_thresholds->critical = NULL;
187
188 if (string == NULL || strcmp(string, "") == 0)
189 return temp_thresholds;
190
191 if((temp_string = strdup(string)) == NULL) {
192 free(temp_thresholds);
193 utils_errno = NP_MEMORY_ERROR;
194 return NULL;
195 }
196
197 /* Find critical part and parse the range */
198 separator = index(temp_string, '/');
199 if (separator) {
200 *separator = '\0';
201 separator++;
202 }
203 if ((strcmp(temp_string, "") != 0) && (temp_range = _parse_range_string_v2( temp_string )) == NULL) {
204 free(temp_thresholds);
205 free(temp_string);
206 /* utils_errno already set */
207 return NULL;
208 }
209 temp_thresholds->critical = temp_range;
210
211 if (separator == NULL || strcmp(separator, "") == 0) {
212 return temp_thresholds;
213 }
214 /* UOM not processed yet */
215 if(( temp_range = _parse_range_string_v2( separator )) == NULL) {
216 free(temp_thresholds);
217 free(temp_string);
218 return NULL;
219 }
220 temp_thresholds->warning = temp_range;
221 return temp_thresholds;
222}
223
224thresholds
225*parse_thresholds_string(char *string)
226{
227 thresholds *my_threshold;
228 if ((my_threshold = _parse_thresholds_string(string)) == NULL)
229 _die_on_parse_error(utils_errno);
230 return my_threshold;
231}
232
101/* returns 0 if okay, otherwise 1 */ 233/* returns 0 if okay, otherwise 1 */
102int 234int
103_set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) 235_set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
@@ -139,8 +271,7 @@ set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_str
139 } 271 }
140} 272}
141 273
142void print_thresholds(const char *threshold_name, thresholds *my_threshold) { 274void print_thresholds(thresholds *my_threshold) {
143 printf("%s - ", threshold_name);
144 if (! my_threshold) { 275 if (! my_threshold) {
145 printf("Threshold not set"); 276 printf("Threshold not set");
146 } else { 277 } else {
diff --git a/lib/utils_base.h b/lib/utils_base.h
index bda7659..c616c5b 100644
--- a/lib/utils_base.h
+++ b/lib/utils_base.h
@@ -31,7 +31,7 @@ typedef struct thresholds_struct {
31range *parse_range_string (char *); 31range *parse_range_string (char *);
32int _set_thresholds(thresholds **, char *, char *); 32int _set_thresholds(thresholds **, char *, char *);
33void set_thresholds(thresholds **, char *, char *); 33void set_thresholds(thresholds **, char *, char *);
34void print_thresholds(const char *, thresholds *); 34void print_thresholds(thresholds *);
35int check_range(double, range *); 35int check_range(double, range *);
36int get_status(double, thresholds *); 36int get_status(double, thresholds *);
37 37
@@ -39,9 +39,22 @@ char *np_escaped_string (const char *);
39 39
40void die (int, const char *, ...) __attribute__((noreturn,format(printf, 2, 3))); 40void die (int, const char *, ...) __attribute__((noreturn,format(printf, 2, 3)));
41 41
42/* Return codes for _set_thresholds */ 42
43#define NP_RANGE_UNPARSEABLE 1 43/* Parses a threshold string, as entered from command line */
44#define NP_WARN_WITHIN_CRIT 2 44/* Returns a malloc'd threshold* which can be freed */
45thresholds *parse_thresholds_string(char *);
46thresholds * _parse_thresholds_string(char *);
47void free_thresholds(thresholds *);
48range * _parse_range_string_v2(char *);
49int utils_errno;
50
51
52/* Error codes */
53#define NP_RANGE_UNPARSEABLE 1
54#define NP_WARN_WITHIN_CRIT 2
55#define NP_RANGE_MISSING_COLON 3
56#define NP_THRESHOLD_UNPARSEABLE 4
57#define NP_MEMORY_ERROR 5
45 58
46/* a simple check to see if we're running as root. 59/* a simple check to see if we're running as root.
47 * returns zero on failure, nonzero on success */ 60 * returns zero on failure, nonzero on success */