svc: remove superfluout INIT_G()
[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         opt_complementary = "vv"; /* -w N, -v is a counter */
510         getopt32(argv, "w:+v", &waitsec, &verbose);
511         argv += optind;
512         action = *argv++;
513         if (!action || !*argv) bb_show_usage();
514
515         tnow = time(NULL) + 0x400000000000000aULL;
516         tstart = tnow;
517         curdir = open(".", O_RDONLY|O_NDELAY);
518         if (curdir == -1)
519                 fatal_cannot("open current directory");
520
521         act = &control;
522         acts = "s";
523         cbk = &check;
524
525         switch (*action) {
526         case 'x':
527         case 'e':
528                 acts = "x";
529                 if (!verbose) cbk = NULL;
530                 break;
531         case 'X':
532         case 'E':
533                 acts = "x";
534                 kll = 1;
535                 break;
536         case 'D':
537                 acts = "d";
538                 kll = 1;
539                 break;
540         case 'T':
541                 acts = "tc";
542                 kll = 1;
543                 break;
544         case 't':
545                 if (str_equal(action, "try-restart")) {
546                         acts = "tc";
547                         break;
548                 }
549         case 'c':
550                 if (str_equal(action, "check")) {
551                         act = NULL;
552                         acts = "C";
553                         break;
554                 }
555         case 'u': case 'd': case 'o': case 'p': case 'h':
556         case 'a': case 'i': case 'k': case 'q': case '1': case '2':
557                 action[1] = '\0';
558                 acts = action;
559                 if (!verbose)
560                         cbk = NULL;
561                 break;
562         case 's':
563                 if (str_equal(action, "shutdown")) {
564                         acts = "x";
565                         break;
566                 }
567                 if (str_equal(action, "start")) {
568                         acts = "u";
569                         break;
570                 }
571                 if (str_equal(action, "stop")) {
572                         acts = "d";
573                         break;
574                 }
575                 /* "status" */
576                 act = &status;
577                 cbk = NULL;
578                 break;
579         case 'r':
580                 if (str_equal(action, "restart")) {
581                         acts = "tcu";
582                         break;
583                 }
584                 if (str_equal(action, "reload")) {
585                         acts = "h";
586                         break;
587                 }
588                 bb_show_usage();
589         case 'f':
590                 if (str_equal(action, "force-reload")) {
591                         acts = "tc";
592                         kll = 1;
593                         break;
594                 }
595                 if (str_equal(action, "force-restart")) {
596                         acts = "tcu";
597                         kll = 1;
598                         break;
599                 }
600                 if (str_equal(action, "force-shutdown")) {
601                         acts = "x";
602                         kll = 1;
603                         break;
604                 }
605                 if (str_equal(action, "force-stop")) {
606                         acts = "d";
607                         kll = 1;
608                         break;
609                 }
610         default:
611                 bb_show_usage();
612         }
613
614         service = argv;
615         while ((x = *service) != NULL) {
616                 if (x[0] != '/' && x[0] != '.'
617                  && x[0] != '\0' && x[strlen(x) - 1] != '/'
618                 ) {
619                         if (chdir(varservice) == -1)
620                                 goto chdir_failed_0;
621                 }
622                 if (chdir(x) == -1) {
623  chdir_failed_0:
624                         fail("can't change to service directory");
625                         goto nullify_service_0;
626                 }
627                 if (act && (act(acts) == -1)) {
628  nullify_service_0:
629                         *service = (char*) -1L; /* "dead" */
630                 }
631                 if (fchdir(curdir) == -1)
632                         fatal_cannot("change to original directory");
633                 service++;
634         }
635
636         if (cbk) while (1) {
637                 int want_exit;
638                 int diff;
639
640                 diff = tnow - tstart;
641                 service = argv;
642                 want_exit = 1;
643                 while ((x = *service) != NULL) {
644                         if (x == (char*) -1L) /* "dead" */
645                                 goto next;
646                         if (x[0] != '/' && x[0] != '.') {
647                                 if (chdir(varservice) == -1)
648                                         goto chdir_failed;
649                         }
650                         if (chdir(x) == -1) {
651  chdir_failed:
652                                 fail("can't change to service directory");
653                                 goto nullify_service;
654                         }
655                         if (cbk(acts) != 0)
656                                 goto nullify_service;
657                         want_exit = 0;
658                         if (diff >= waitsec) {
659                                 printf(kll ? "kill: " : "timeout: ");
660                                 if (svstatus_get() > 0) {
661                                         svstatus_print(x);
662                                         ++rc;
663                                 }
664                                 bb_putchar('\n'); /* will also flush the output */
665                                 if (kll)
666                                         control("k");
667  nullify_service:
668                                 *service = (char*) -1L; /* "dead" */
669                         }
670                         if (fchdir(curdir) == -1)
671                                 fatal_cannot("change to original directory");
672  next:
673                         service++;
674                 }
675                 if (want_exit) break;
676                 usleep(420000);
677                 tnow = time(NULL) + 0x400000000000000aULL;
678         }
679         return rc > 99 ? 99 : rc;
680 }
681
682 #if ENABLE_SV
683 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
684 int sv_main(int argc UNUSED_PARAM, char **argv)
685 {
686         return sv(argv);
687 }
688 #endif
689
690 //usage:#define svc_trivial_usage
691 //usage:       "[-udopchaitkx] SERVICE_DIR..."
692 //usage:#define svc_full_usage "\n\n"
693 //usage:       "Control services monitored by runsv supervisor"
694 //usage:   "\n"
695 //usage:   "\n""        -u      If service is not running, start it; restart if it stops"
696 //usage:   "\n""        -d      If service is running, send TERM+CONT signals; do not restart it"
697 //usage:   "\n""        -o      Once: if service is not running, start it; do not restart it"
698 //usage:   "\n""        -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
699 //usage:   "\n""        -x      Exit: runsv will exit as soon as the service is down"
700 #if ENABLE_SVC
701 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
702 int svc_main(int argc UNUSED_PARAM, char **argv)
703 {
704         char command[2];
705         const char *optstring;
706         unsigned opts;
707
708         optstring = "udopchaitkx";
709         opts = getopt32(argv, optstring);
710         argv += optind;
711         if (!argv[0] || !opts)
712                 bb_show_usage();
713
714         argv -= 2;
715         if (optind > 2) {
716                 argv--;
717                 argv[2] = (char*)"--";
718         }
719         argv[0] = (char*)"sv";
720         argv[1] = command;
721         command[1] = '\0';
722
723         do {
724                 if (opts & 1) {
725                         int r;
726
727                         command[0] = *optstring;
728
729                         /* getopt() was already called by getopt32():
730                          * reset the libc getopt() function's internal state.
731                          */
732                         GETOPT_RESET();
733                         r = sv(argv);
734                         if (r)
735                                 return 1;
736                 }
737                 optstring++;
738                 opts >>= 1;
739         } while (opts);
740
741         return 0;
742 }
743 #endif