getopt32: remove opt_complementary
[oweals/busybox.git] / runit / sv.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8    1. Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10    2. Redistributions in binary form must reproduce the above copyright
11       notice, this list of conditions and the following disclaimer in the
12       documentation and/or other materials provided with the distribution.
13    3. The name of the author may not be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Taken from http://smarden.org/runit/sv.8.html:
29
30 sv - control and manage services monitored by runsv
31
32 sv [-v] [-w sec] command services
33 /etc/init.d/service [-w sec] command
34
35 The sv program reports the current status and controls the state of services
36 monitored by the runsv(8) supervisor.
37
38 services consists of one or more arguments, each argument naming a directory
39 service used by runsv(8). If service doesn't start with a dot or slash and
40 doesn't end with a slash, it is searched in the default services directory
41 /var/service/, otherwise relative to the current directory.
42
43 command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
44 1, 2, term, kill, or exit, or start, stop, reload, restart, shutdown,
45 force-stop, force-reload, force-restart, force-shutdown, try-restart.
46
47 status
48     Report the current status of the service, and the appendant log service
49     if available, to standard output.
50 up
51     If the service is not running, start it. If the service stops, restart it.
52 down
53     If the service is running, send it the TERM signal, and the CONT signal.
54     If ./run exits, start ./finish if it exists. After it stops, do not
55     restart service.
56 once
57     If the service is not running, start it. Do not restart it if it stops.
58 pause cont hup alarm interrupt quit 1 2 term kill
59     If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
60     USR1, USR2, TERM, or KILL signal respectively.
61 exit
62     If the service is running, send it the TERM signal, and the CONT signal.
63     Do not restart the service. If the service is down, and no log service
64     exists, runsv(8) exits. If the service is down and a log service exists,
65     runsv(8) closes the standard input of the log service and waits for it to
66     terminate. If the log service is down, runsv(8) exits. This command is
67     ignored if it is given to an appendant log service.
68
69 sv actually looks only at the first character of above commands.
70
71 Commands compatible to LSB init script actions:
72
73 status
74     Same as status.
75 start
76     Same as up, but wait up to 7 seconds for the command to take effect.
77     Then report the status or timeout. If the script ./check exists in
78     the service directory, sv runs this script to check whether the service
79     is up and available; it's considered to be available if ./check exits
80     with 0.
81 stop
82     Same as down, but wait up to 7 seconds for the service to become down.
83     Then report the status or timeout.
84 reload
85     Same as hup, and additionally report the status afterwards.
86 restart
87     Send the commands term, cont, and up to the service, and wait up to
88     7 seconds for the service to restart. Then report the status or timeout.
89     If the script ./check exists in the service directory, sv runs this script
90     to check whether the service is up and available again; it's considered
91     to be available if ./check exits with 0.
92 shutdown
93     Same as exit, but wait up to 7 seconds for the runsv(8) process
94     to terminate. Then report the status or timeout.
95 force-stop
96     Same as down, but wait up to 7 seconds for the service to become down.
97     Then report the status, and on timeout send the service the kill command.
98 force-reload
99     Send the service the term and cont commands, and wait up to
100     7 seconds for the service to restart. Then report the status,
101     and on timeout send the service the kill command.
102 force-restart
103     Send the service the term, cont and up commands, and wait up to
104     7 seconds for the service to restart. Then report the status, and
105     on timeout send the service the kill command. If the script ./check
106     exists in the service directory, sv runs this script to check whether
107     the service is up and available again; it's considered to be available
108     if ./check exits with 0.
109 force-shutdown
110     Same as exit, but wait up to 7 seconds for the runsv(8) process to
111     terminate. Then report the status, and on timeout send the service
112     the kill command.
113 try-restart
114     if the service is running, send it the term and cont commands, and wait up to
115     7 seconds for the service to restart. Then report the status or timeout.
116
117 Additional Commands
118
119 check
120     Check for the service to be in the state that's been requested. Wait up to
121     7 seconds for the service to reach the requested state, then report
122     the status or timeout. If the requested state of the service is up,
123     and the script ./check exists in the service directory, sv runs
124     this script to check whether the service is up and running;
125     it's considered to be up if ./check exits with 0.
126
127 Options
128
129 -v
130     If the command is up, down, term, once, cont, or exit, then wait up to 7
131     seconds for the command to take effect. Then report the status or timeout.
132 -w sec
133     Override the default timeout of 7 seconds with sec seconds. Implies -v.
134
135 Environment
136
137 SVDIR
138     The environment variable $SVDIR overrides the default services directory
139     /var/service.
140 SVWAIT
141     The environment variable $SVWAIT overrides the default 7 seconds to wait
142     for a command to take effect. It is overridden by the -w option.
143
144 Exit Codes
145     sv exits 0, if the command was successfully sent to all services, and,
146     if it was told to wait, the command has taken effect to all services.
147
148     For each service that caused an error (e.g. the directory is not
149     controlled by a runsv(8) process, or sv timed out while waiting),
150     sv increases the exit code by one and exits non zero. The maximum
151     is 99. sv exits 100 on error.
152 */
153
154 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
155
156 //config:config SV
157 //config:       bool "sv (7.8 kb)"
158 //config:       default y
159 //config:       help
160 //config:       sv reports the current status and controls the state of services
161 //config:       monitored by the runsv supervisor.
162 //config:
163 //config:config SV_DEFAULT_SERVICE_DIR
164 //config:       string "Default directory for services"
165 //config:       default "/var/service"
166 //config:       depends on SV
167 //config:       help
168 //config:       Default directory for services.
169 //config:       Defaults to "/var/service"
170 //config:
171 //config:config SVC
172 //config:       bool "svc (7.8 kb)"
173 //config:       default y
174 //config:       help
175 //config:       svc controls the state of services monitored by the runsv supervisor.
176 //config:       It is comaptible with daemontools command with the same name.
177
178 //applet:IF_SV( APPLET_NOEXEC(sv,  sv,  BB_DIR_USR_BIN, BB_SUID_DROP, sv ))
179 //applet:IF_SVC(APPLET_NOEXEC(svc, svc, BB_DIR_USR_BIN, BB_SUID_DROP, svc))
180
181 //kbuild:lib-$(CONFIG_SV) += sv.o
182 //kbuild:lib-$(CONFIG_SVC) += sv.o
183
184 #include <sys/file.h>
185 #include "libbb.h"
186 #include "common_bufsiz.h"
187 #include "runit_lib.h"
188
189 struct globals {
190         const char *acts;
191         char **service;
192         unsigned rc;
193 /* "Bernstein" time format: unix + 0x400000000000000aULL */
194         uint64_t tstart, tnow;
195         svstatus_t svstatus;
196         smallint islog;
197 } FIX_ALIASING;
198 #define G (*(struct globals*)bb_common_bufsiz1)
199 #define acts         (G.acts        )
200 #define service      (G.service     )
201 #define rc           (G.rc          )
202 #define tstart       (G.tstart      )
203 #define tnow         (G.tnow        )
204 #define svstatus     (G.svstatus    )
205 #define islog        (G.islog       )
206 #define INIT_G() do { \
207         setup_common_bufsiz(); \
208         /* need to zero out, svc calls sv() repeatedly */ \
209         memset(&G, 0, sizeof(G)); \
210 } while (0)
211
212
213 #define str_equal(s,t) (strcmp((s), (t)) == 0)
214
215
216 static void fatal_cannot(const char *m1) NORETURN;
217 static void fatal_cannot(const char *m1)
218 {
219         bb_perror_msg("fatal: can't %s", m1);
220         _exit(151);
221 }
222
223 static void out(const char *p, const char *m1)
224 {
225         printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
226         if (errno) {
227                 printf(": %s", strerror(errno));
228         }
229         bb_putchar('\n'); /* will also flush the output */
230 }
231
232 #define WARN    "warning: "
233 #define OK      "ok: "
234
235 static void fail(const char *m1)
236 {
237         ++rc;
238         out("fail: ", m1);
239 }
240 static void failx(const char *m1)
241 {
242         errno = 0;
243         fail(m1);
244 }
245 static void warn(const char *m1)
246 {
247         ++rc;
248         /* "warning: <service>: <m1>\n" */
249         out("warning: ", m1);
250 }
251 static void ok(const char *m1)
252 {
253         errno = 0;
254         out(OK, m1);
255 }
256
257 static int svstatus_get(void)
258 {
259         int fd, r;
260
261         fd = open("supervise/ok", O_WRONLY|O_NDELAY);
262         if (fd == -1) {
263                 if (errno == ENODEV) {
264                         *acts == 'x' ? ok("runsv not running")
265                                      : failx("runsv not running");
266                         return 0;
267                 }
268                 warn("can't open supervise/ok");
269                 return -1;
270         }
271         close(fd);
272         fd = open("supervise/status", O_RDONLY|O_NDELAY);
273         if (fd == -1) {
274                 warn("can't open supervise/status");
275                 return -1;
276         }
277         r = read(fd, &svstatus, 20);
278         close(fd);
279         switch (r) {
280         case 20:
281                 break;
282         case -1:
283                 warn("can't read supervise/status");
284                 return -1;
285         default:
286                 errno = 0;
287                 warn("can't read supervise/status: bad format");
288                 return -1;
289         }
290         return 1;
291 }
292
293 static unsigned svstatus_print(const char *m)
294 {
295         int diff;
296         int pid;
297         int normallyup = 0;
298         struct stat s;
299         uint64_t timestamp;
300
301         if (stat("down", &s) == -1) {
302                 if (errno != ENOENT) {
303                         bb_perror_msg(WARN"can't stat %s/down", *service);
304                         return 0;
305                 }
306                 normallyup = 1;
307         }
308         pid = SWAP_LE32(svstatus.pid_le32);
309         timestamp = SWAP_BE64(svstatus.time_be64);
310         switch (svstatus.run_or_finish) {
311                 case 0: printf("down: "); break;
312                 case 1: printf("run: "); break;
313                 case 2: printf("finish: "); break;
314         }
315         printf("%s: ", m);
316         if (svstatus.run_or_finish)
317                 printf("(pid %d) ", pid);
318         diff = tnow - timestamp;
319         printf("%us", (diff < 0 ? 0 : diff));
320         if (pid) {
321                 if (!normallyup) printf(", normally down");
322                 if (svstatus.paused) printf(", paused");
323                 if (svstatus.want == 'd') printf(", want down");
324                 if (svstatus.got_term) printf(", got TERM");
325         } else {
326                 if (normallyup) printf(", normally up");
327                 if (svstatus.want == 'u') printf(", want up");
328         }
329         return pid ? 1 : 2;
330 }
331
332 static int status(const char *unused UNUSED_PARAM)
333 {
334         int r;
335
336         if (svstatus_get() <= 0)
337                 return 0;
338
339         r = svstatus_print(*service);
340         islog = 1;
341         if (chdir("log") == -1) {
342                 if (errno != ENOENT) {
343                         printf("; ");
344                         warn("can't change directory");
345                 } else
346                         bb_putchar('\n');
347         } else {
348                 printf("; ");
349                 if (svstatus_get()) {
350                         r = svstatus_print("log");
351                         bb_putchar('\n');
352                 }
353         }
354         islog = 0;
355         return r;
356 }
357
358 static int checkscript(void)
359 {
360         char *prog[2];
361         struct stat s;
362         int pid, w;
363
364         if (stat("check", &s) == -1) {
365                 if (errno == ENOENT) return 1;
366                 bb_perror_msg(WARN"can't stat %s/check", *service);
367                 return 0;
368         }
369         /* if (!(s.st_mode & S_IXUSR)) return 1; */
370         prog[0] = (char*)"./check";
371         prog[1] = NULL;
372         pid = spawn(prog);
373         if (pid <= 0) {
374                 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
375                 return 0;
376         }
377         while (safe_waitpid(pid, &w, 0) == -1) {
378                 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
379                 return 0;
380         }
381         return WEXITSTATUS(w) == 0;
382 }
383
384 static int check(const char *a)
385 {
386         int r;
387         unsigned pid_le32;
388         uint64_t timestamp;
389
390         r = svstatus_get();
391         if (r == -1)
392                 return -1;
393         while (*a) {
394                 if (r == 0) {
395                         if (*a == 'x')
396                                 return 1;
397                         return -1;
398                 }
399                 pid_le32 = svstatus.pid_le32;
400                 switch (*a) {
401                 case 'x':
402                         return 0;
403                 case 'u':
404                         if (!pid_le32 || svstatus.run_or_finish != 1)
405                                 return 0;
406                         if (!checkscript())
407                                 return 0;
408                         break;
409                 case 'd':
410                         if (pid_le32 || svstatus.run_or_finish != 0)
411                                 return 0;
412                         break;
413                 case 'C':
414                         if (pid_le32 && !checkscript())
415                                 return 0;
416                         break;
417                 case 't':
418                 case 'k':
419                         if (!pid_le32 && svstatus.want == 'd')
420                                 break;
421                         timestamp = SWAP_BE64(svstatus.time_be64);
422                         if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
423                                 return 0;
424                         break;
425                 case 'o':
426                         timestamp = SWAP_BE64(svstatus.time_be64);
427                         if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
428                                 return 0;
429                         break;
430                 case 'p':
431                         if (pid_le32 && !svstatus.paused)
432                                 return 0;
433                         break;
434                 case 'c':
435                         if (pid_le32 && svstatus.paused)
436                                 return 0;
437                         break;
438                 }
439                 ++a;
440         }
441         printf(OK);
442         svstatus_print(*service);
443         bb_putchar('\n'); /* will also flush the output */
444         return 1;
445 }
446
447 static int control(const char *a)
448 {
449         int fd, r, l;
450
451         if (svstatus_get() <= 0)
452                 return -1;
453         if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
454                 return 0;
455         fd = open("supervise/control", O_WRONLY|O_NDELAY);
456         if (fd == -1) {
457                 if (errno != ENODEV)
458                         warn("can't open supervise/control");
459                 else
460                         *a == 'x' ? ok("runsv not running") : failx("runsv not running");
461                 return -1;
462         }
463         l = strlen(a);
464         r = write(fd, a, l);
465         close(fd);
466         if (r != l) {
467                 warn("can't write to supervise/control");
468                 return -1;
469         }
470         return 1;
471 }
472
473 //usage:#define sv_trivial_usage
474 //usage:       "[-v] [-w SEC] CMD SERVICE_DIR..."
475 //usage:#define sv_full_usage "\n\n"
476 //usage:       "Control services monitored by runsv supervisor.\n"
477 //usage:       "Commands (only first character is enough):\n"
478 //usage:       "\n"
479 //usage:       "status: query service status\n"
480 //usage:       "up: if service isn't running, start it. If service stops, restart it\n"
481 //usage:       "once: like 'up', but if service stops, don't restart it\n"
482 //usage:       "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
483 //usage:       "        if it exists. After it stops, don't restart service\n"
484 //usage:       "exit: send TERM and CONT signals to service and log service. If they exit,\n"
485 //usage:       "        runsv exits too\n"
486 //usage:       "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
487 //usage:       "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
488 static int sv(char **argv)
489 {
490         char *x;
491         char *action;
492         const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
493         unsigned waitsec = 7;
494         smallint kll = 0;
495         int verbose = 0;
496         int (*act)(const char*);
497         int (*cbk)(const char*);
498         int curdir;
499
500         INIT_G();
501
502         xfunc_error_retval = 100;
503
504         x = getenv("SVDIR");
505         if (x) varservice = x;
506         x = getenv("SVWAIT");
507         if (x) waitsec = xatou(x);
508
509         getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
510                         &waitsec, &verbose
511         );
512         argv += optind;
513         action = *argv++;
514         if (!action || !*argv) bb_show_usage();
515
516         tnow = time(NULL) + 0x400000000000000aULL;
517         tstart = tnow;
518         curdir = open(".", O_RDONLY|O_NDELAY);
519         if (curdir == -1)
520                 fatal_cannot("open current directory");
521
522         act = &control;
523         acts = "s";
524         cbk = &check;
525
526         switch (*action) {
527         case 'x':
528         case 'e':
529                 acts = "x";
530                 if (!verbose) cbk = NULL;
531                 break;
532         case 'X':
533         case 'E':
534                 acts = "x";
535                 kll = 1;
536                 break;
537         case 'D':
538                 acts = "d";
539                 kll = 1;
540                 break;
541         case 'T':
542                 acts = "tc";
543                 kll = 1;
544                 break;
545         case 't':
546                 if (str_equal(action, "try-restart")) {
547                         acts = "tc";
548                         break;
549                 }
550         case 'c':
551                 if (str_equal(action, "check")) {
552                         act = NULL;
553                         acts = "C";
554                         break;
555                 }
556         case 'u': case 'd': case 'o': case 'p': case 'h':
557         case 'a': case 'i': case 'k': case 'q': case '1': case '2':
558                 action[1] = '\0';
559                 acts = action;
560                 if (!verbose)
561                         cbk = NULL;
562                 break;
563         case 's':
564                 if (str_equal(action, "shutdown")) {
565                         acts = "x";
566                         break;
567                 }
568                 if (str_equal(action, "start")) {
569                         acts = "u";
570                         break;
571                 }
572                 if (str_equal(action, "stop")) {
573                         acts = "d";
574                         break;
575                 }
576                 /* "status" */
577                 act = &status;
578                 cbk = NULL;
579                 break;
580         case 'r':
581                 if (str_equal(action, "restart")) {
582                         acts = "tcu";
583                         break;
584                 }
585                 if (str_equal(action, "reload")) {
586                         acts = "h";
587                         break;
588                 }
589                 bb_show_usage();
590         case 'f':
591                 if (str_equal(action, "force-reload")) {
592                         acts = "tc";
593                         kll = 1;
594                         break;
595                 }
596                 if (str_equal(action, "force-restart")) {
597                         acts = "tcu";
598                         kll = 1;
599                         break;
600                 }
601                 if (str_equal(action, "force-shutdown")) {
602                         acts = "x";
603                         kll = 1;
604                         break;
605                 }
606                 if (str_equal(action, "force-stop")) {
607                         acts = "d";
608                         kll = 1;
609                         break;
610                 }
611         default:
612                 bb_show_usage();
613         }
614
615         service = argv;
616         while ((x = *service) != NULL) {
617                 if (x[0] != '/' && x[0] != '.'
618                  && x[0] != '\0' && x[strlen(x) - 1] != '/'
619                 ) {
620                         if (chdir(varservice) == -1)
621                                 goto chdir_failed_0;
622                 }
623                 if (chdir(x) == -1) {
624  chdir_failed_0:
625                         fail("can't change to service directory");
626                         goto nullify_service_0;
627                 }
628                 if (act && (act(acts) == -1)) {
629  nullify_service_0:
630                         *service = (char*) -1L; /* "dead" */
631                 }
632                 if (fchdir(curdir) == -1)
633                         fatal_cannot("change to original directory");
634                 service++;
635         }
636
637         if (cbk) while (1) {
638                 int want_exit;
639                 int diff;
640
641                 diff = tnow - tstart;
642                 service = argv;
643                 want_exit = 1;
644                 while ((x = *service) != NULL) {
645                         if (x == (char*) -1L) /* "dead" */
646                                 goto next;
647                         if (x[0] != '/' && x[0] != '.') {
648                                 if (chdir(varservice) == -1)
649                                         goto chdir_failed;
650                         }
651                         if (chdir(x) == -1) {
652  chdir_failed:
653                                 fail("can't change to service directory");
654                                 goto nullify_service;
655                         }
656                         if (cbk(acts) != 0)
657                                 goto nullify_service;
658                         want_exit = 0;
659                         if (diff >= waitsec) {
660                                 printf(kll ? "kill: " : "timeout: ");
661                                 if (svstatus_get() > 0) {
662                                         svstatus_print(x);
663                                         ++rc;
664                                 }
665                                 bb_putchar('\n'); /* will also flush the output */
666                                 if (kll)
667                                         control("k");
668  nullify_service:
669                                 *service = (char*) -1L; /* "dead" */
670                         }
671                         if (fchdir(curdir) == -1)
672                                 fatal_cannot("change to original directory");
673  next:
674                         service++;
675                 }
676                 if (want_exit) break;
677                 usleep(420000);
678                 tnow = time(NULL) + 0x400000000000000aULL;
679         }
680         return rc > 99 ? 99 : rc;
681 }
682
683 #if ENABLE_SV
684 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
685 int sv_main(int argc UNUSED_PARAM, char **argv)
686 {
687         return sv(argv);
688 }
689 #endif
690
691 //usage:#define svc_trivial_usage
692 //usage:       "[-udopchaitkx] SERVICE_DIR..."
693 //usage:#define svc_full_usage "\n\n"
694 //usage:       "Control services monitored by runsv supervisor"
695 //usage:   "\n"
696 //usage:   "\n""        -u      If service is not running, start it; restart if it stops"
697 //usage:   "\n""        -d      If service is running, send TERM+CONT signals; do not restart it"
698 //usage:   "\n""        -o      Once: if service is not running, start it; do not restart it"
699 //usage:   "\n""        -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
700 //usage:   "\n""        -x      Exit: runsv will exit as soon as the service is down"
701 #if ENABLE_SVC
702 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
703 int svc_main(int argc UNUSED_PARAM, char **argv)
704 {
705         char command[2];
706         const char *optstring;
707         unsigned opts;
708
709         optstring = "udopchaitkx";
710         opts = getopt32(argv, optstring);
711         argv += optind;
712         if (!argv[0] || !opts)
713                 bb_show_usage();
714
715         argv -= 2;
716         if (optind > 2) {
717                 argv--;
718                 argv[2] = (char*)"--";
719         }
720         argv[0] = (char*)"sv";
721         argv[1] = command;
722         command[1] = '\0';
723
724         do {
725                 if (opts & 1) {
726                         int r;
727
728                         command[0] = *optstring;
729
730                         /* getopt() was already called by getopt32():
731                          * reset the libc getopt() function's internal state.
732                          */
733                         GETOPT_RESET();
734                         r = sv(argv);
735                         if (r)
736                                 return 1;
737                 }
738                 optstring++;
739                 opts >>= 1;
740         } while (opts);
741
742         return 0;
743 }
744 #endif