aboutsummaryrefslogtreecommitdiff
path: root/runit
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-12-27 04:35:04 +0000
commit8d42f86b146871ae4c4cafd3801a85f381249a14 (patch)
treeb963999fc54eddb65f1929b894f868e24851fc9c /runit
downloadbusybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.gz
busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.tar.bz2
busybox-w32-8d42f86b146871ae4c4cafd3801a85f381249a14.zip
Correcting branch name to be like previous ones
Diffstat (limited to 'runit')
-rw-r--r--runit/Config.in66
-rw-r--r--runit/Kbuild12
-rw-r--r--runit/chpst.c373
-rw-r--r--runit/runit_lib.c982
-rw-r--r--runit/runit_lib.h403
-rw-r--r--runit/runsv.c613
-rw-r--r--runit/runsvdir.c306
-rw-r--r--runit/sv.c360
-rw-r--r--runit/svlogd.c878
9 files changed, 3993 insertions, 0 deletions
diff --git a/runit/Config.in b/runit/Config.in
new file mode 100644
index 000000000..8a7deea72
--- /dev/null
+++ b/runit/Config.in
@@ -0,0 +1,66 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Runit Utilities"
7
8config RUNSV
9 bool "runsv"
10 default n
11 help
12 runsv starts and monitors a service and optionally an appendant log
13 service.
14
15config RUNSVDIR
16 bool "runsvdir"
17 default n
18 help
19 runsvdir starts a runsv process for each subdirectory, or symlink to
20 a directory, in the services directory dir, up to a limit of 1000
21 subdirectories, and restarts a runsv process if it terminates.
22
23config SV
24 bool "sv"
25 default n
26 help
27 sv reports the current status and controls the state of services
28 monitored by the runsv supervisor.
29
30config SVLOGD
31 bool "svlogd"
32 default n
33 help
34 svlogd continuously reads log data from its standard input, optionally
35 filters log messages, and writes the data to one or more automatically
36 rotated logs.
37
38config CHPST
39 bool "chpst"
40 default n
41 help
42 chpst changes the process state according to the given options, and
43 execs specified program.
44
45config SETUIDGID
46 bool "setuidgid"
47 help
48 Sets soft resource limits as specified by options
49
50config ENVUIDGID
51 bool "envuidgid"
52 help
53 Sets $UID to account's uid and $GID to account's gid
54
55config ENVDIR
56 bool "envdir"
57 help
58 Sets various environment variables as specified by files
59 in the given directory
60
61config SOFTLIMIT
62 bool "softlimit"
63 help
64 Sets soft resource limits as specified by options
65
66endmenu
diff --git a/runit/Kbuild b/runit/Kbuild
new file mode 100644
index 000000000..ad1706cb6
--- /dev/null
+++ b/runit/Kbuild
@@ -0,0 +1,12 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under the GPL v2, see the file LICENSE in this tarball.
6
7lib-y:=
8lib-$(CONFIG_RUNSV) += runsv.o runit_lib.o
9lib-$(CONFIG_RUNSVDIR) += runsvdir.o runit_lib.o
10lib-$(CONFIG_SV) += sv.o runit_lib.o
11lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o
12lib-$(CONFIG_CHPST) += chpst.o
diff --git a/runit/chpst.c b/runit/chpst.c
new file mode 100644
index 000000000..f8e63031f
--- /dev/null
+++ b/runit/chpst.c
@@ -0,0 +1,373 @@
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* Dependencies on runit_lib.c removed */
30
31#include "busybox.h"
32
33#include <dirent.h>
34
35// Must match constants in chpst_main!
36#define OPT_verbose (option_mask32 & 0x2000)
37#define OPT_pgrp (option_mask32 & 0x4000)
38#define OPT_nostdin (option_mask32 & 0x8000)
39#define OPT_nostdout (option_mask32 & 0x10000)
40#define OPT_nostderr (option_mask32 & 0x20000)
41
42static char *set_user;
43static char *env_user;
44static const char *env_dir;
45static long limitd = -2;
46static long limits = -2;
47static long limitl = -2;
48static long limita = -2;
49static long limito = -2;
50static long limitp = -2;
51static long limitf = -2;
52static long limitc = -2;
53static long limitr = -2;
54static long limitt = -2;
55static int nicelvl;
56static const char *root;
57
58static void suidgid(char *user)
59{
60 struct bb_uidgid_t ugid;
61
62 if (!uidgid_get(&ugid, user)) {
63 bb_error_msg_and_die("unknown user/group: %s", user);
64 }
65 if (setgroups(1, &ugid.gid) == -1)
66 bb_perror_msg_and_die("setgroups");
67 xsetgid(ugid.gid);
68 xsetuid(ugid.uid);
69}
70
71static void euidgid(char *user)
72{
73 struct bb_uidgid_t ugid;
74
75 if (!uidgid_get(&ugid, user)) {
76 bb_error_msg_and_die("unknown user/group: %s", user);
77 }
78 xsetenv("GID", utoa(ugid.gid));
79 xsetenv("UID", utoa(ugid.uid));
80}
81
82static void edir(const char *directory_name)
83{
84 int wdir;
85 DIR *dir;
86 struct dirent *d;
87 int fd;
88
89 wdir = xopen(".", O_RDONLY | O_NDELAY);
90 xchdir(directory_name);
91 dir = opendir(".");
92 if (!dir)
93 bb_perror_msg_and_die("opendir %s", directory_name);
94 for (;;) {
95 errno = 0;
96 d = readdir(dir);
97 if (!d) {
98 if (errno)
99 bb_perror_msg_and_die("readdir %s",
100 directory_name);
101 break;
102 }
103 if (d->d_name[0] == '.') continue;
104 fd = open(d->d_name, O_RDONLY | O_NDELAY);
105 if (fd < 0) {
106 if ((errno == EISDIR) && env_dir) {
107 if (OPT_verbose)
108 bb_perror_msg("warning: %s/%s is a directory",
109 directory_name, d->d_name);
110 continue;
111 } else
112 bb_perror_msg_and_die("open %s/%s",
113 directory_name, d->d_name);
114 }
115 if (fd >= 0) {
116 char buf[256];
117 char *tail;
118 int size;
119
120 size = safe_read(fd, buf, sizeof(buf)-1);
121 if (size < 0)
122 bb_perror_msg_and_die("read %s/%s",
123 directory_name, d->d_name);
124 if (size == 0) {
125 unsetenv(d->d_name);
126 continue;
127 }
128 buf[size] = '\n';
129 tail = memchr(buf, '\n', sizeof(buf));
130 /* skip trailing whitespace */;
131 while (1) {
132 if (tail[0]==' ') tail[0] = '\0';
133 if (tail[0]=='\t') tail[0] = '\0';
134 if (tail[0]=='\n') tail[0] = '\0';
135 if (tail == buf) break;
136 tail--;
137 }
138 xsetenv(d->d_name, buf);
139 }
140 }
141 closedir(dir);
142 if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
143 close(wdir);
144}
145
146static void limit(int what, long l)
147{
148 struct rlimit r;
149
150 if (getrlimit(what, &r) == -1) bb_perror_msg_and_die("getrlimit");
151 if ((l < 0) || (l > r.rlim_max))
152 r.rlim_cur = r.rlim_max;
153 else
154 r.rlim_cur = l;
155 if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit");
156}
157
158static void slimit(void)
159{
160 if (limitd >= -1) {
161#ifdef RLIMIT_DATA
162 limit(RLIMIT_DATA, limitd);
163#else
164 if (OPT_verbose) bb_error_msg("system does not support %s",
165 "RLIMIT_DATA");
166#endif
167 }
168 if (limits >= -1) {
169#ifdef RLIMIT_STACK
170 limit(RLIMIT_STACK, limits);
171#else
172 if (OPT_verbose) bb_error_msg("system does not support %s",
173 "RLIMIT_STACK");
174#endif
175 }
176 if (limitl >= -1) {
177#ifdef RLIMIT_MEMLOCK
178 limit(RLIMIT_MEMLOCK, limitl);
179#else
180 if (OPT_verbose) bb_error_msg("system does not support %s",
181 "RLIMIT_MEMLOCK");
182#endif
183 }
184 if (limita >= -1) {
185#ifdef RLIMIT_VMEM
186 limit(RLIMIT_VMEM, limita);
187#else
188#ifdef RLIMIT_AS
189 limit(RLIMIT_AS, limita);
190#else
191 if (OPT_verbose)
192 bb_error_msg("system does not support %s",
193 "RLIMIT_VMEM");
194#endif
195#endif
196 }
197 if (limito >= -1) {
198#ifdef RLIMIT_NOFILE
199 limit(RLIMIT_NOFILE, limito);
200#else
201#ifdef RLIMIT_OFILE
202 limit(RLIMIT_OFILE, limito);
203#else
204 if (OPT_verbose)
205 bb_error_msg("system does not support %s",
206 "RLIMIT_NOFILE");
207#endif
208#endif
209 }
210 if (limitp >= -1) {
211#ifdef RLIMIT_NPROC
212 limit(RLIMIT_NPROC, limitp);
213#else
214 if (OPT_verbose) bb_error_msg("system does not support %s",
215 "RLIMIT_NPROC");
216#endif
217 }
218 if (limitf >= -1) {
219#ifdef RLIMIT_FSIZE
220 limit(RLIMIT_FSIZE, limitf);
221#else
222 if (OPT_verbose) bb_error_msg("system does not support %s",
223 "RLIMIT_FSIZE");
224#endif
225 }
226 if (limitc >= -1) {
227#ifdef RLIMIT_CORE
228 limit(RLIMIT_CORE, limitc);
229#else
230 if (OPT_verbose) bb_error_msg("system does not support %s",
231 "RLIMIT_CORE");
232#endif
233 }
234 if (limitr >= -1) {
235#ifdef RLIMIT_RSS
236 limit(RLIMIT_RSS, limitr);
237#else
238 if (OPT_verbose) bb_error_msg("system does not support %s",
239 "RLIMIT_RSS");
240#endif
241 }
242 if (limitt >= -1) {
243#ifdef RLIMIT_CPU
244 limit(RLIMIT_CPU, limitt);
245#else
246 if (OPT_verbose) bb_error_msg("system does not support %s",
247 "RLIMIT_CPU");
248#endif
249 }
250}
251
252/* argv[0] */
253static void setuidgid(int, char **);
254static void envuidgid(int, char **);
255static void envdir(int, char **);
256static void softlimit(int, char **);
257
258int chpst_main(int argc, char **argv)
259{
260 if (applet_name[3] == 'd') envdir(argc, argv);
261 if (applet_name[1] == 'o') softlimit(argc, argv);
262 if (applet_name[0] == 's') setuidgid(argc, argv);
263 if (applet_name[0] == 'e') envuidgid(argc, argv);
264 // otherwise we are chpst
265
266 {
267 char *m,*d,*o,*p,*f,*c,*r,*t,*n;
268 getopt32(argc, argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
269 &set_user,&env_user,&env_dir,
270 &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
271 // if (option_mask32 & 0x1) // -u
272 // if (option_mask32 & 0x2) // -U
273 // if (option_mask32 & 0x4) // -e
274 if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
275 if (option_mask32 & 0x10) limitd = xatoul(d); // -d
276 if (option_mask32 & 0x20) limito = xatoul(o); // -o
277 if (option_mask32 & 0x40) limitp = xatoul(p); // -p
278 if (option_mask32 & 0x80) limitf = xatoul(f); // -f
279 if (option_mask32 & 0x100) limitc = xatoul(c); // -c
280 if (option_mask32 & 0x200) limitr = xatoul(r); // -r
281 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
282 // if (option_mask32 & 0x800) // -/
283 if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
284 // The below consts should match #defines at top!
285 //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
286 //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
287 //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
288 //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
289 //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
290 }
291 argv += optind;
292 if (!argv || !*argv) bb_show_usage();
293
294 if (OPT_pgrp) setsid();
295 if (env_dir) edir(env_dir);
296 if (root) {
297 xchdir(root);
298 if (chroot(".") == -1)
299 bb_perror_msg_and_die("chroot");
300 }
301 slimit();
302 if (nicelvl) {
303 errno = 0;
304 if (nice(nicelvl) == -1)
305 bb_perror_msg_and_die("nice");
306 }
307 if (env_user) euidgid(env_user);
308 if (set_user) suidgid(set_user);
309 if (OPT_nostdin) close(0);
310 if (OPT_nostdout) close(1);
311 if (OPT_nostderr) close(2);
312 execvp(argv[0], argv);
313 bb_perror_msg_and_die("exec %s", argv[0]);
314}
315
316static void setuidgid(int argc, char **argv)
317{
318 const char *account;
319
320 account = *++argv;
321 if (!account) bb_show_usage();
322 if (!*++argv) bb_show_usage();
323 suidgid((char*)account);
324 execvp(argv[0], argv);
325 bb_perror_msg_and_die("exec %s", argv[0]);
326}
327
328static void envuidgid(int argc, char **argv)
329{
330 const char *account;
331
332 account = *++argv;
333 if (!account) bb_show_usage();
334 if (!*++argv) bb_show_usage();
335 euidgid((char*)account);
336 execvp(argv[0], argv);
337 bb_perror_msg_and_die("exec %s", argv[0]);
338}
339
340static void envdir(int argc, char **argv)
341{
342 const char *dir;
343
344 dir = *++argv;
345 if (!dir) bb_show_usage();
346 if (!*++argv) bb_show_usage();
347 edir(dir);
348 execvp(argv[0], argv);
349 bb_perror_msg_and_die("exec %s", argv[0]);
350}
351
352static void softlimit(int argc, char **argv)
353{
354 char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
355 getopt32(argc, argv, "+a:c:d:f:l:m:o:p:r:s:t:",
356 &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
357 if (option_mask32 & 0x001) limita = xatoul(a); // -a
358 if (option_mask32 & 0x002) limitc = xatoul(c); // -c
359 if (option_mask32 & 0x004) limitd = xatoul(d); // -d
360 if (option_mask32 & 0x008) limitf = xatoul(f); // -f
361 if (option_mask32 & 0x010) limitl = xatoul(l); // -l
362 if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
363 if (option_mask32 & 0x040) limito = xatoul(o); // -o
364 if (option_mask32 & 0x080) limitp = xatoul(p); // -p
365 if (option_mask32 & 0x100) limitr = xatoul(r); // -r
366 if (option_mask32 & 0x200) limits = xatoul(s); // -s
367 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
368 argv += optind;
369 if (!argv[0]) bb_show_usage();
370 slimit();
371 execvp(argv[0], argv);
372 bb_perror_msg_and_die("exec %s", argv[0]);
373}
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
new file mode 100644
index 000000000..5ebbc5840
--- /dev/null
+++ b/runit/runit_lib.c
@@ -0,0 +1,982 @@
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* Collected into one file from runit's many tiny files */
30/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */
31
32#include <sys/poll.h>
33#include <sys/file.h>
34#include "libbb.h"
35#include "runit_lib.h"
36
37/*** buffer.c ***/
38
39void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
40{
41 s->x = buf;
42 s->fd = fd;
43 s->op = op;
44 s->p = 0;
45 s->n = len;
46}
47
48
49/*** buffer_get.c ***/
50
51static int oneread(int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
52{
53 int r;
54
55 for (;;) {
56 r = op(fd,buf,len);
57 if (r == -1) if (errno == EINTR) continue;
58 return r;
59 }
60}
61
62static int getthis(buffer *s,char *buf,unsigned len)
63{
64 if (len > s->p) len = s->p;
65 s->p -= len;
66 memcpy(buf,s->x + s->n,len);
67 s->n += len;
68 return len;
69}
70
71int buffer_feed(buffer *s)
72{
73 int r;
74
75 if (s->p) return s->p;
76 r = oneread(s->op,s->fd,s->x,s->n);
77 if (r <= 0) return r;
78 s->p = r;
79 s->n -= r;
80 if (s->n > 0) memmove(s->x + s->n,s->x,r);
81 return r;
82}
83
84int buffer_bget(buffer *s,char *buf,unsigned len)
85{
86 int r;
87
88 if (s->p > 0) return getthis(s,buf,len);
89 if (s->n <= len) return oneread(s->op,s->fd,buf,s->n);
90 r = buffer_feed(s); if (r <= 0) return r;
91 return getthis(s,buf,len);
92}
93
94int buffer_get(buffer *s,char *buf,unsigned len)
95{
96 int r;
97
98 if (s->p > 0) return getthis(s,buf,len);
99 if (s->n <= len) return oneread(s->op,s->fd,buf,len);
100 r = buffer_feed(s); if (r <= 0) return r;
101 return getthis(s,buf,len);
102}
103
104char *buffer_peek(buffer *s)
105{
106 return s->x + s->n;
107}
108
109void buffer_seek(buffer *s,unsigned len)
110{
111 s->n += len;
112 s->p -= len;
113}
114
115
116/*** buffer_put.c ***/
117
118static int allwrite(int (*op)(int fd,char *buf,unsigned len),int fd,const char *buf,unsigned len)
119{
120 int w;
121
122 while (len) {
123 w = op(fd,(char*)buf,len);
124 if (w == -1) {
125 if (errno == EINTR) continue;
126 return -1; /* note that some data may have been written */
127 }
128 if (w == 0) ; /* luser's fault */
129 buf += w;
130 len -= w;
131 }
132 return 0;
133}
134
135int buffer_flush(buffer *s)
136{
137 int p;
138
139 p = s->p;
140 if (!p) return 0;
141 s->p = 0;
142 return allwrite(s->op,s->fd,s->x,p);
143}
144
145int buffer_putalign(buffer *s,const char *buf,unsigned len)
146{
147 unsigned n;
148
149 while (len > (n = s->n - s->p)) {
150 memcpy(s->x + s->p,buf,n);
151 s->p += n;
152 buf += n;
153 len -= n;
154 if (buffer_flush(s) == -1) return -1;
155 }
156 /* now len <= s->n - s->p */
157 memcpy(s->x + s->p,buf,len);
158 s->p += len;
159 return 0;
160}
161
162int buffer_put(buffer *s,const char *buf,unsigned len)
163{
164 unsigned n;
165
166 n = s->n;
167 if (len > n - s->p) {
168 if (buffer_flush(s) == -1) return -1;
169 /* now s->p == 0 */
170 if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE;
171 while (len > s->n) {
172 if (n > len) n = len;
173 if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
174 buf += n;
175 len -= n;
176 }
177 }
178 /* now len <= s->n - s->p */
179 memcpy(s->x + s->p,buf,len);
180 s->p += len;
181 return 0;
182}
183
184int buffer_putflush(buffer *s,const char *buf,unsigned len)
185{
186 if (buffer_flush(s) == -1) return -1;
187 return allwrite(s->op,s->fd,buf,len);
188}
189
190int buffer_putsalign(buffer *s,const char *buf)
191{
192 return buffer_putalign(s,buf,strlen(buf));
193}
194
195int buffer_puts(buffer *s,const char *buf)
196{
197 return buffer_put(s,buf,strlen(buf));
198}
199
200int buffer_putsflush(buffer *s,const char *buf)
201{
202 return buffer_putflush(s,buf,strlen(buf));
203}
204
205
206/*** buffer_read.c ***/
207
208int buffer_unixread(int fd,char *buf,unsigned len)
209{
210 return read(fd,buf,len);
211}
212
213
214/*** buffer_write.c ***/
215
216int buffer_unixwrite(int fd,char *buf,unsigned len)
217{
218 return write(fd,buf,len);
219}
220
221
222/*** byte_chr.c ***/
223
224unsigned byte_chr(char *s,unsigned n,int c)
225{
226 char ch;
227 char *t;
228
229 ch = c;
230 t = s;
231 for (;;) {
232 if (!n) break; if (*t == ch) break; ++t; --n;
233 if (!n) break; if (*t == ch) break; ++t; --n;
234 if (!n) break; if (*t == ch) break; ++t; --n;
235 if (!n) break; if (*t == ch) break; ++t; --n;
236 }
237 return t - s;
238}
239
240
241/*** coe.c ***/
242
243int coe(int fd)
244{
245 return fcntl(fd,F_SETFD,FD_CLOEXEC);
246}
247
248
249/*** fd_copy.c ***/
250
251int fd_copy(int to,int from)
252{
253 if (to == from) return 0;
254 if (fcntl(from,F_GETFL,0) == -1) return -1;
255 close(to);
256 if (fcntl(from,F_DUPFD,to) == -1) return -1;
257 return 0;
258}
259
260
261/*** fd_move.c ***/
262
263int fd_move(int to,int from)
264{
265 if (to == from) return 0;
266 if (fd_copy(to,from) == -1) return -1;
267 close(from);
268 return 0;
269}
270
271
272/*** fifo.c ***/
273
274int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); }
275
276
277/*** fmt_ptime.c ***/
278
279unsigned fmt_ptime(char *s, struct taia *ta) {
280 struct tm *t;
281 unsigned long u;
282
283 if (ta->sec.x < 4611686018427387914ULL) return 0; /* impossible? */
284 u = ta->sec.x -4611686018427387914ULL;
285 if (!(t = gmtime((time_t*)&u))) return 0;
286 fmt_ulong(s, 1900 + t->tm_year);
287 s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2);
288 s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2);
289 s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2);
290 s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2);
291 s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2);
292 s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9);
293 return 25;
294}
295
296unsigned fmt_taia(char *s, struct taia *t) {
297 static char hex[16] = "0123456789abcdef";
298 static char pack[TAIA_PACK];
299 int i;
300
301 taia_pack(pack, t);
302 s[0] = '@';
303 for (i = 0; i < 12; ++i) {
304 s[i*2+1] = hex[(pack[i] >> 4) &15];
305 s[i*2+2] = hex[pack[i] &15];
306 }
307 return 25;
308}
309
310
311/*** fmt_uint.c ***/
312
313unsigned fmt_uint(char *s,unsigned u)
314{
315 return fmt_ulong(s,u);
316}
317
318
319/*** fmt_uint0.c ***/
320
321unsigned fmt_uint0(char *s,unsigned u,unsigned n)
322{
323 unsigned len;
324 len = fmt_uint(FMT_LEN,u);
325 while (len < n) { if (s) *s++ = '0'; ++len; }
326 if (s) fmt_uint(s,u);
327 return len;
328}
329
330
331/*** fmt_ulong.c ***/
332
333unsigned fmt_ulong(char *s,unsigned long u)
334{
335 unsigned len; unsigned long q;
336 len = 1; q = u;
337 while (q > 9) { ++len; q /= 10; }
338 if (s) {
339 s += len;
340 do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
341 }
342 return len;
343}
344
345
346/*** tai_now.c ***/
347
348void tai_now(struct tai *t)
349{
350 tai_unix(t,time((time_t *) 0));
351}
352
353
354/*** tai_pack.c ***/
355
356void tai_pack(char *s,const struct tai *t)
357{
358 uint64_t x;
359
360 x = t->x;
361 s[7] = x & 255; x >>= 8;
362 s[6] = x & 255; x >>= 8;
363 s[5] = x & 255; x >>= 8;
364 s[4] = x & 255; x >>= 8;
365 s[3] = x & 255; x >>= 8;
366 s[2] = x & 255; x >>= 8;
367 s[1] = x & 255; x >>= 8;
368 s[0] = x;
369}
370
371
372/*** tai_sub.c ***/
373
374void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
375{
376 t->x = u->x - v->x;
377}
378
379
380/*** tai_unpack.c ***/
381
382void tai_unpack(const char *s,struct tai *t)
383{
384 uint64_t x;
385
386 x = (unsigned char) s[0];
387 x <<= 8; x += (unsigned char) s[1];
388 x <<= 8; x += (unsigned char) s[2];
389 x <<= 8; x += (unsigned char) s[3];
390 x <<= 8; x += (unsigned char) s[4];
391 x <<= 8; x += (unsigned char) s[5];
392 x <<= 8; x += (unsigned char) s[6];
393 x <<= 8; x += (unsigned char) s[7];
394 t->x = x;
395}
396
397
398/*** taia_add.c ***/
399
400/* XXX: breaks tai encapsulation */
401
402void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
403{
404 t->sec.x = u->sec.x + v->sec.x;
405 t->nano = u->nano + v->nano;
406 t->atto = u->atto + v->atto;
407 if (t->atto > 999999999UL) {
408 t->atto -= 1000000000UL;
409 ++t->nano;
410 }
411 if (t->nano > 999999999UL) {
412 t->nano -= 1000000000UL;
413 ++t->sec.x;
414 }
415}
416
417
418/*** taia_approx.c ***/
419
420double taia_approx(const struct taia *t)
421{
422 return tai_approx(&t->sec) + taia_frac(t);
423}
424
425
426/*** taia_frac.c ***/
427
428double taia_frac(const struct taia *t)
429{
430 return (t->atto * 0.000000001 + t->nano) * 0.000000001;
431}
432
433
434/*** taia_less.c ***/
435
436/* XXX: breaks tai encapsulation */
437
438int taia_less(const struct taia *t,const struct taia *u)
439{
440 if (t->sec.x < u->sec.x) return 1;
441 if (t->sec.x > u->sec.x) return 0;
442 if (t->nano < u->nano) return 1;
443 if (t->nano > u->nano) return 0;
444 return t->atto < u->atto;
445}
446
447
448/*** taia_now.c ***/
449
450void taia_now(struct taia *t)
451{
452 struct timeval now;
453 gettimeofday(&now,(struct timezone *) 0);
454 tai_unix(&t->sec,now.tv_sec);
455 t->nano = 1000 * now.tv_usec + 500;
456 t->atto = 0;
457}
458
459
460/*** taia_pack.c ***/
461
462void taia_pack(char *s,const struct taia *t)
463{
464 unsigned long x;
465
466 tai_pack(s,&t->sec);
467 s += 8;
468
469 x = t->atto;
470 s[7] = x & 255; x >>= 8;
471 s[6] = x & 255; x >>= 8;
472 s[5] = x & 255; x >>= 8;
473 s[4] = x;
474 x = t->nano;
475 s[3] = x & 255; x >>= 8;
476 s[2] = x & 255; x >>= 8;
477 s[1] = x & 255; x >>= 8;
478 s[0] = x;
479}
480
481
482/*** taia_sub.c ***/
483
484/* XXX: breaks tai encapsulation */
485
486void taia_sub(struct taia *t,const struct taia *u,const struct taia *v)
487{
488 unsigned long unano = u->nano;
489 unsigned long uatto = u->atto;
490
491 t->sec.x = u->sec.x - v->sec.x;
492 t->nano = unano - v->nano;
493 t->atto = uatto - v->atto;
494 if (t->atto > uatto) {
495 t->atto += 1000000000UL;
496 --t->nano;
497 }
498 if (t->nano > unano) {
499 t->nano += 1000000000UL;
500 --t->sec.x;
501 }
502}
503
504
505/*** taia_uint.c ***/
506
507/* XXX: breaks tai encapsulation */
508
509void taia_uint(struct taia *t,unsigned s)
510{
511 t->sec.x = s;
512 t->nano = 0;
513 t->atto = 0;
514}
515
516
517/*** stralloc_cat.c ***/
518#if 0
519
520int stralloc_cat(stralloc *sato,const stralloc *safrom)
521{
522 return stralloc_catb(sato,safrom->s,safrom->len);
523}
524
525
526/*** stralloc_catb.c ***/
527
528int stralloc_catb(stralloc *sa,const char *s,unsigned n)
529{
530 if (!sa->s) return stralloc_copyb(sa,s,n);
531 if (!stralloc_readyplus(sa,n + 1)) return 0;
532 memcpy(sa->s + sa->len,s,n);
533 sa->len += n;
534 sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
535 return 1;
536}
537
538
539/*** stralloc_cats.c ***/
540
541int stralloc_cats(stralloc *sa,const char *s)
542{
543 return stralloc_catb(sa,s,strlen(s));
544}
545
546
547/*** stralloc_eady.c ***/
548
549GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
550GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
551
552
553/*** stralloc_opyb.c ***/
554
555int stralloc_copyb(stralloc *sa,const char *s,unsigned n)
556{
557 if (!stralloc_ready(sa,n + 1)) return 0;
558 memcpy(sa->s,s,n);
559 sa->len = n;
560 sa->s[n] = 'Z'; /* ``offensive programming'' */
561 return 1;
562}
563
564
565/*** stralloc_opys.c ***/
566
567int stralloc_copys(stralloc *sa,const char *s)
568{
569 return stralloc_copyb(sa,s,strlen(s));
570}
571
572
573/*** stralloc_pend.c ***/
574
575GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
576
577#endif /* stralloc */
578
579/*** iopause.c ***/
580
581void iopause(iopause_fd *x,unsigned len,struct taia *deadline,struct taia *stamp)
582{
583 struct taia t;
584 int millisecs;
585 double d;
586 int i;
587
588 if (taia_less(deadline,stamp))
589 millisecs = 0;
590 else {
591 t = *stamp;
592 taia_sub(&t,deadline,&t);
593 d = taia_approx(&t);
594 if (d > 1000.0) d = 1000.0;
595 millisecs = d * 1000.0 + 20.0;
596 }
597
598 for (i = 0;i < len;++i)
599 x[i].revents = 0;
600
601 poll(x,len,millisecs);
602 /* XXX: some kernels apparently need x[0] even if len is 0 */
603 /* XXX: how to handle EAGAIN? are kernels really this dumb? */
604 /* XXX: how to handle EINVAL? when exactly can this happen? */
605}
606
607
608/*** lock_ex.c ***/
609
610int lock_ex(int fd)
611{
612 return flock(fd,LOCK_EX);
613}
614
615
616/*** lock_exnb.c ***/
617
618int lock_exnb(int fd)
619{
620 return flock(fd,LOCK_EX | LOCK_NB);
621}
622
623
624/*** open_append.c ***/
625
626int open_append(const char *fn)
627{
628 return open(fn, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
629}
630
631
632/*** open_read.c ***/
633
634int open_read(const char *fn)
635{
636 return open(fn, O_RDONLY|O_NDELAY);
637}
638
639
640/*** open_trunc.c ***/
641
642int open_trunc(const char *fn)
643{
644 return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644);
645}
646
647
648/*** open_write.c ***/
649
650int open_write(const char *fn)
651{
652 return open(fn, O_WRONLY|O_NDELAY);
653}
654
655
656/*** openreadclose.c ***/
657#if 0
658int openreadclose(const char *fn,stralloc *sa,unsigned bufsize)
659{
660 int fd;
661 fd = open_read(fn);
662 if (fd == -1) {
663 if (errno == ENOENT) return 0;
664 return -1;
665 }
666 if (readclose(fd,sa,bufsize) == -1) return -1;
667 return 1;
668}
669#endif
670
671
672/*** pathexec_env.c ***/
673#if 0
674static stralloc plus;
675static stralloc tmp;
676
677int pathexec_env(const char *s,const char *t)
678{
679 if (!s) return 1;
680 if (!stralloc_copys(&tmp,s)) return 0;
681 if (t) {
682 if (!stralloc_cats(&tmp,"=")) return 0;
683 if (!stralloc_cats(&tmp,t)) return 0;
684 }
685 if (!stralloc_0(&tmp)) return 0;
686 return stralloc_cat(&plus,&tmp);
687}
688
689void pathexec(char **argv)
690{
691 char **e;
692 unsigned elen;
693 unsigned i;
694 unsigned j;
695 unsigned split;
696 unsigned t;
697
698 if (!stralloc_cats(&plus,"")) return;
699
700 elen = 0;
701 for (i = 0;environ[i];++i)
702 ++elen;
703 for (i = 0;i < plus.len;++i)
704 if (!plus.s[i])
705 ++elen;
706
707 e = malloc((elen + 1) * sizeof(char *));
708 if (!e) return;
709
710 elen = 0;
711 for (i = 0;environ[i];++i)
712 e[elen++] = environ[i];
713
714 j = 0;
715 for (i = 0;i < plus.len;++i)
716 if (!plus.s[i]) {
717 split = str_chr(plus.s + j,'=');
718 for (t = 0;t < elen;++t)
719 if (memcmp(plus.s + j,e[t],split) == 0)
720 if (e[t][split] == '=') {
721 --elen;
722 e[t] = e[elen];
723 break;
724 }
725 if (plus.s[j + split])
726 e[elen++] = plus.s + j;
727 j = i + 1;
728 }
729 e[elen] = 0;
730
731 pathexec_run(*argv,argv,e);
732 free(e);
733}
734#endif
735
736/*** pathexec_run.c ***/
737#if 0
738static stralloc tmp;
739
740void pathexec_run(const char *file,char *const *argv,char *const *envp)
741{
742 const char *path;
743 unsigned split;
744 int savederrno;
745
746 if (file[str_chr(file,'/')]) {
747 execve(file,argv,envp);
748 return;
749 }
750
751 path = getenv("PATH");
752 if (!path) path = "/bin:/usr/bin";
753
754 savederrno = 0;
755 for (;;) {
756 split = str_chr(path,':');
757 if (!stralloc_copyb(&tmp,path,split)) return;
758 if (!split)
759 if (!stralloc_cats(&tmp,".")) return;
760 if (!stralloc_cats(&tmp,"/")) return;
761 if (!stralloc_cats(&tmp,file)) return;
762 if (!stralloc_0(&tmp)) return;
763
764 execve(tmp.s,argv,envp);
765 if (errno != ENOENT) {
766 savederrno = errno;
767 if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return;
768 }
769
770 if (!path[split]) {
771 if (savederrno) errno = savederrno;
772 return;
773 }
774 path += split;
775 path += 1;
776 }
777}
778#endif
779
780/*** pmatch.c ***/
781
782unsigned pmatch(const char *p, const char *s, unsigned len) {
783 for (;;) {
784 char c = *p++;
785 if (!c) return !len;
786 switch (c) {
787 case '*':
788 if (!(c = *p)) return 1;
789 for (;;) {
790 if (!len) return 0;
791 if (*s == c) break;
792 ++s; --len;
793 }
794 continue;
795 case '+':
796 if ((c = *p++) != *s) return 0;
797 for (;;) {
798 if (!len) return 1;
799 if (*s != c) break;
800 ++s; --len;
801 }
802 continue;
803 /*
804 case '?':
805 if (*p == '?') {
806 if (*s != '?') return 0;
807 ++p;
808 }
809 ++s; --len;
810 continue;
811 */
812 default:
813 if (!len) return 0;
814 if (*s != c) return 0;
815 ++s; --len;
816 continue;
817 }
818 }
819 return 0;
820}
821
822
823/*** prot.c ***/
824
825int prot_gid(int gid)
826{
827 gid_t x = gid;
828 if (setgroups(1,&x) == -1) return -1;
829 return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
830}
831
832int prot_uid(int uid)
833{
834 return setuid(uid);
835}
836
837
838/*** readclose.c ***/
839#if 0
840int readclose_append(int fd,stralloc *sa,unsigned bufsize)
841{
842 int r;
843 for (;;) {
844 if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
845 r = read(fd,sa->s + sa->len,bufsize);
846 if (r == -1) if (errno == EINTR) continue;
847 if (r <= 0) { close(fd); return r; }
848 sa->len += r;
849 }
850}
851
852int readclose(int fd,stralloc *sa,unsigned bufsize)
853{
854 if (!stralloc_copys(sa,"")) { close(fd); return -1; }
855 return readclose_append(fd,sa,bufsize);
856}
857#endif
858
859/*** scan_ulong.c ***/
860
861unsigned scan_ulong(const char *s,unsigned long *u)
862{
863 unsigned pos = 0;
864 unsigned long result = 0;
865 unsigned long c;
866 while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
867 result = result * 10 + c;
868 ++pos;
869 }
870 *u = result;
871 return pos;
872}
873
874
875/*** seek_set.c ***/
876
877int seek_set(int fd,seek_pos pos)
878{
879 if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0;
880}
881
882
883/*** sig.c ***/
884
885int sig_alarm = SIGALRM;
886int sig_child = SIGCHLD;
887int sig_cont = SIGCONT;
888int sig_hangup = SIGHUP;
889int sig_int = SIGINT;
890int sig_pipe = SIGPIPE;
891int sig_term = SIGTERM;
892
893void (*sig_defaulthandler)(int) = SIG_DFL;
894void (*sig_ignorehandler)(int) = SIG_IGN;
895
896
897/*** sig_block.c ***/
898
899void sig_block(int sig)
900{
901 sigset_t ss;
902 sigemptyset(&ss);
903 sigaddset(&ss,sig);
904 sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
905}
906
907void sig_unblock(int sig)
908{
909 sigset_t ss;
910 sigemptyset(&ss);
911 sigaddset(&ss,sig);
912 sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
913}
914
915void sig_blocknone(void)
916{
917 sigset_t ss;
918 sigemptyset(&ss);
919 sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
920}
921
922
923/*** sig_catch.c ***/
924
925void sig_catch(int sig,void (*f)(int))
926{
927 struct sigaction sa;
928 sa.sa_handler = f;
929 sa.sa_flags = 0;
930 sigemptyset(&sa.sa_mask);
931 sigaction(sig,&sa,(struct sigaction *) 0);
932}
933
934
935/*** sig_pause.c ***/
936
937void sig_pause(void)
938{
939 sigset_t ss;
940 sigemptyset(&ss);
941 sigsuspend(&ss);
942}
943
944
945/*** str_chr.c ***/
946
947unsigned str_chr(const char *s,int c)
948{
949 char ch;
950 const char *t;
951
952 ch = c;
953 t = s;
954 for (;;) {
955 if (!*t) break; if (*t == ch) break; ++t;
956 if (!*t) break; if (*t == ch) break; ++t;
957 if (!*t) break; if (*t == ch) break; ++t;
958 if (!*t) break; if (*t == ch) break; ++t;
959 }
960 return t - s;
961}
962
963
964/*** wait_nohang.c ***/
965
966int wait_nohang(int *wstat)
967{
968 return waitpid(-1,wstat,WNOHANG);
969}
970
971
972/*** wait_pid.c ***/
973
974int wait_pid(int *wstat, int pid)
975{
976 int r;
977
978 do
979 r = waitpid(pid,wstat,0);
980 while ((r == -1) && (errno == EINTR));
981 return r;
982}
diff --git a/runit/runit_lib.h b/runit/runit_lib.h
new file mode 100644
index 000000000..1f911919d
--- /dev/null
+++ b/runit/runit_lib.h
@@ -0,0 +1,403 @@
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/*** buffer.h ***/
29
30typedef struct buffer {
31 char *x;
32 unsigned p;
33 unsigned n;
34 int fd;
35 int (*op)(int fd,char *buf,unsigned len);
36} buffer;
37
38#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
39#define BUFFER_INSIZE 8192
40#define BUFFER_OUTSIZE 8192
41
42extern void buffer_init(buffer *,int (*)(int fd,char *buf,unsigned len),int,char *,unsigned);
43
44extern int buffer_flush(buffer *);
45extern int buffer_put(buffer *,const char *,unsigned);
46extern int buffer_putalign(buffer *,const char *,unsigned);
47extern int buffer_putflush(buffer *,const char *,unsigned);
48extern int buffer_puts(buffer *,const char *);
49extern int buffer_putsalign(buffer *,const char *);
50extern int buffer_putsflush(buffer *,const char *);
51
52#define buffer_PUTC(s,c) \
53 ( ((s)->n != (s)->p) \
54 ? ( (s)->x[(s)->p++] = (c), 0 ) \
55 : buffer_put((s),&(c),1) \
56 )
57
58extern int buffer_get(buffer *,char *,unsigned);
59extern int buffer_bget(buffer *,char *,unsigned);
60extern int buffer_feed(buffer *);
61
62extern char *buffer_peek(buffer *);
63extern void buffer_seek(buffer *,unsigned);
64
65#define buffer_PEEK(s) ( (s)->x + (s)->n )
66#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
67
68#define buffer_GETC(s,c) \
69 ( ((s)->p > 0) \
70 ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \
71 : buffer_get((s),(c),1) \
72 )
73
74extern int buffer_copy(buffer *,buffer *);
75
76extern int buffer_unixread(int,char *,unsigned);
77/* Actually, int buffer_unixwrite(int,const char *,unsigned),
78 but that 'const' will produce warnings... oh well */
79extern int buffer_unixwrite(int,char *,unsigned);
80
81
82/*** byte.h ***/
83
84extern unsigned byte_chr(char *s,unsigned n,int c);
85
86
87/*** coe.h ***/
88
89extern int coe(int);
90
91
92/*** direntry.h ***/
93
94#define direntry struct dirent
95
96
97/*** fd.h ***/
98
99extern int fd_copy(int,int);
100extern int fd_move(int,int);
101
102
103/*** fifo.h ***/
104
105extern int fifo_make(const char *,int);
106
107
108/*** fmt.h ***/
109
110#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
111#define FMT_LEN ((char *) 0) /* convenient abbreviation */
112
113extern unsigned fmt_uint(char *,unsigned);
114extern unsigned fmt_uint0(char *,unsigned,unsigned);
115extern unsigned fmt_xint(char *,unsigned);
116extern unsigned fmt_nbbint(char *,unsigned,unsigned,unsigned,unsigned);
117extern unsigned fmt_ushort(char *,unsigned short);
118extern unsigned fmt_xshort(char *,unsigned short);
119extern unsigned fmt_nbbshort(char *,unsigned,unsigned,unsigned,unsigned short);
120extern unsigned fmt_ulong(char *,unsigned long);
121extern unsigned fmt_xlong(char *,unsigned long);
122extern unsigned fmt_nbblong(char *,unsigned,unsigned,unsigned,unsigned long);
123
124extern unsigned fmt_plusminus(char *,int);
125extern unsigned fmt_minus(char *,int);
126extern unsigned fmt_0x(char *,int);
127
128extern unsigned fmt_str(char *,const char *);
129extern unsigned fmt_strn(char *,const char *,unsigned);
130
131
132/*** tai.h ***/
133
134struct tai {
135 uint64_t x;
136} ;
137
138#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u)))
139
140extern void tai_now(struct tai *);
141
142#define tai_approx(t) ((double) ((t)->x))
143
144extern void tai_add(struct tai *,const struct tai *,const struct tai *);
145extern void tai_sub(struct tai *,const struct tai *,const struct tai *);
146#define tai_less(t,u) ((t)->x < (u)->x)
147
148#define TAI_PACK 8
149extern void tai_pack(char *,const struct tai *);
150extern void tai_unpack(const char *,struct tai *);
151
152extern void tai_uint(struct tai *,unsigned);
153
154
155/*** taia.h ***/
156
157struct taia {
158 struct tai sec;
159 unsigned long nano; /* 0...999999999 */
160 unsigned long atto; /* 0...999999999 */
161} ;
162
163extern void taia_tai(const struct taia *,struct tai *);
164
165extern void taia_now(struct taia *);
166
167extern double taia_approx(const struct taia *);
168extern double taia_frac(const struct taia *);
169
170extern void taia_add(struct taia *,const struct taia *,const struct taia *);
171extern void taia_addsec(struct taia *,const struct taia *,int);
172extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
173extern void taia_half(struct taia *,const struct taia *);
174extern int taia_less(const struct taia *,const struct taia *);
175
176#define TAIA_PACK 16
177extern void taia_pack(char *,const struct taia *);
178extern void taia_unpack(const char *,struct taia *);
179
180#define TAIA_FMTFRAC 19
181extern unsigned taia_fmtfrac(char *,const struct taia *);
182
183extern void taia_uint(struct taia *,unsigned);
184
185
186/*** fmt_ptime.h ***/
187
188#define FMT_PTIME 30
189
190extern unsigned fmt_ptime(char *, struct taia *);
191extern unsigned fmt_taia(char *, struct taia *);
192
193
194/*** gen_alloc.h ***/
195
196#define GEN_ALLOC_typedef(ta,type,field,len,a) \
197 typedef struct ta { type *field; unsigned len; unsigned a; } ta;
198
199
200/*** gen_allocdefs.h ***/
201
202#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
203int ta_ready(ta *x,unsigned n) \
204{ unsigned i; \
205 if (x->field) { \
206 i = x->a; \
207 if (n > i) { \
208 x->a = base + n + (n >> 3); \
209 x->field = realloc(x->field,x->a * sizeof(type)); \
210 if (x->field) return 1; \
211 x->a = i; return 0; } \
212 return 1; } \
213 x->len = 0; \
214 return !!(x->field = malloc((x->a = n) * sizeof(type))); }
215
216#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
217int ta_rplus(ta *x,unsigned n) \
218{ unsigned i; \
219 if (x->field) { \
220 i = x->a; n += x->len; \
221 if (n > i) { \
222 x->a = base + n + (n >> 3); \
223 x->field = realloc(x->field,x->a * sizeof(type)); \
224 if (x->field) return 1; \
225 x->a = i; return 0; } \
226 return 1; } \
227 x->len = 0; \
228 return !!(x->field = malloc((x->a = n) * sizeof(type))); }
229
230#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
231int ta_append(ta *x,const type *i) \
232{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
233
234
235/*** stralloc.h ***/
236#if 0
237GEN_ALLOC_typedef(stralloc,char,s,len,a)
238
239extern int stralloc_ready(stralloc *,unsigned);
240extern int stralloc_readyplus(stralloc *,unsigned);
241extern int stralloc_copy(stralloc *,const stralloc *);
242extern int stralloc_cat(stralloc *,const stralloc *);
243extern int stralloc_copys(stralloc *,const char *);
244extern int stralloc_cats(stralloc *,const char *);
245extern int stralloc_copyb(stralloc *,const char *,unsigned);
246extern int stralloc_catb(stralloc *,const char *,unsigned);
247extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */
248extern int stralloc_starts(stralloc *,const char *);
249
250#define stralloc_0(sa) stralloc_append(sa,"")
251
252extern int stralloc_catulong0(stralloc *,unsigned long,unsigned);
253extern int stralloc_catlong0(stralloc *,long,unsigned);
254
255#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0))
256#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n)))
257#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n)))
258#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0))
259#endif
260
261/*** iopause.h ***/
262
263typedef struct pollfd iopause_fd;
264#define IOPAUSE_READ POLLIN
265#define IOPAUSE_WRITE POLLOUT
266
267extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *);
268
269
270/*** lock.h ***/
271
272extern int lock_ex(int);
273extern int lock_un(int);
274extern int lock_exnb(int);
275
276
277/*** ndelay.h ***/
278
279extern int ndelay_on(int);
280extern int ndelay_off(int);
281
282
283/*** open.h ***/
284
285extern int open_read(const char *);
286extern int open_excl(const char *);
287extern int open_append(const char *);
288extern int open_trunc(const char *);
289extern int open_write(const char *);
290
291
292/*** openreadclose.h ***/
293#if 0
294extern int openreadclose(const char *,stralloc *,unsigned);
295#endif
296
297/*** pathexec.h ***/
298
299extern void pathexec_run(const char *,char *const *,char *const *);
300extern int pathexec_env(const char *,const char *);
301extern void pathexec(char **);
302
303
304/*** pmatch.h ***/
305
306extern unsigned pmatch(const char *, const char *, unsigned);
307
308
309/*** prot.h ***/
310
311extern int prot_gid(int);
312extern int prot_uid(int);
313
314
315/*** readclose.h ***/
316#if 0
317extern int readclose_append(int,stralloc *,unsigned);
318extern int readclose(int,stralloc *,unsigned);
319#endif
320
321/*** scan.h ***/
322
323extern unsigned scan_uint(const char *,unsigned *);
324extern unsigned scan_xint(const char *,unsigned *);
325extern unsigned scan_nbbint(const char *,unsigned,unsigned,unsigned,unsigned *);
326extern unsigned scan_ushort(const char *,unsigned short *);
327extern unsigned scan_xshort(const char *,unsigned short *);
328extern unsigned scan_nbbshort(const char *,unsigned,unsigned,unsigned,unsigned short *);
329extern unsigned scan_ulong(const char *,unsigned long *);
330extern unsigned scan_xlong(const char *,unsigned long *);
331extern unsigned scan_nbblong(const char *,unsigned,unsigned,unsigned,unsigned long *);
332
333extern unsigned scan_plusminus(const char *,int *);
334extern unsigned scan_0x(const char *,unsigned *);
335
336extern unsigned scan_whitenskip(const char *,unsigned);
337extern unsigned scan_nonwhitenskip(const char *,unsigned);
338extern unsigned scan_charsetnskip(const char *,const char *,unsigned);
339extern unsigned scan_noncharsetnskip(const char *,const char *,unsigned);
340
341extern unsigned scan_strncmp(const char *,const char *,unsigned);
342extern unsigned scan_memcmp(const char *,const char *,unsigned);
343
344extern unsigned scan_long(const char *,long *);
345extern unsigned scan_8long(const char *,unsigned long *);
346
347
348/*** seek.h ***/
349
350typedef unsigned long seek_pos;
351
352extern seek_pos seek_cur(int);
353
354extern int seek_set(int,seek_pos);
355extern int seek_end(int);
356
357extern int seek_trunc(int,seek_pos);
358
359#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
360
361
362/*** sig.h ***/
363
364extern int sig_alarm;
365extern int sig_child;
366extern int sig_cont;
367extern int sig_hangup;
368extern int sig_int;
369extern int sig_pipe;
370extern int sig_term;
371
372extern void (*sig_defaulthandler)(int);
373extern void (*sig_ignorehandler)(int);
374
375extern void sig_catch(int,void (*)(int));
376#define sig_ignore(s) (sig_catch((s),sig_ignorehandler))
377#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler))
378
379extern void sig_block(int);
380extern void sig_unblock(int);
381extern void sig_blocknone(void);
382extern void sig_pause(void);
383
384extern void sig_dfl(int);
385
386
387/*** str.h ***/
388
389extern unsigned str_chr(const char *,int); /* never returns NULL */
390
391#define str_diff(s,t) strcmp((s),(t))
392#define str_equal(s,t) (!strcmp((s),(t)))
393
394
395/*** wait.h ***/
396
397extern int wait_pid(int *wstat, int pid);
398extern int wait_nohang(int *wstat);
399
400#define wait_crashed(w) ((w) & 127)
401#define wait_exitcode(w) ((w) >> 8)
402#define wait_stopsig(w) ((w) >> 8)
403#define wait_stopped(w) (((w) & 127) == 127)
diff --git a/runit/runsv.c b/runit/runsv.c
new file mode 100644
index 000000000..e1b5459fb
--- /dev/null
+++ b/runit/runsv.c
@@ -0,0 +1,613 @@
1/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
9static int selfpipe[2];
10
11/* state */
12#define S_DOWN 0
13#define S_RUN 1
14#define S_FINISH 2
15/* ctrl */
16#define C_NOOP 0
17#define C_TERM 1
18#define C_PAUSE 2
19/* want */
20#define W_UP 0
21#define W_DOWN 1
22#define W_EXIT 2
23
24struct svdir {
25 int pid;
26 int state;
27 int ctrl;
28 int want;
29 struct taia start;
30 int fdlock;
31 int fdcontrol;
32 int fdcontrolwrite;
33 int islog;
34};
35static struct svdir svd[2];
36
37static int sigterm = 0;
38static int haslog = 0;
39static int pidchanged = 1;
40static int logpipe[2];
41static char *dir;
42
43#define usage() bb_show_usage()
44
45static void fatal2_cannot(char *m1, char *m2)
46{
47 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
48 /* was exiting 111 */
49}
50static void fatal_cannot(char *m)
51{
52 fatal2_cannot(m, "");
53 /* was exiting 111 */
54}
55static void fatal2x_cannot(char *m1, char *m2)
56{
57 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
58 /* was exiting 111 */
59}
60static void warn_cannot(char *m)
61{
62 bb_perror_msg("%s: warning: cannot %s", dir, m);
63}
64static void warnx_cannot(char *m)
65{
66 bb_error_msg("%s: warning: cannot %s", dir, m);
67}
68
69static void stopservice(struct svdir *);
70
71static void s_child(int sig_no)
72{
73 write(selfpipe[1], "", 1);
74}
75
76static void s_term(int sig_no)
77{
78 sigterm = 1;
79 write(selfpipe[1], "", 1); /* XXX */
80}
81
82static char *add_str(char *p, const char *to_add)
83{
84 while ((*p = *to_add) != '\0') {
85 p++;
86 to_add++;
87 }
88 return p;
89}
90
91static int open_trunc_or_warn(const char *name)
92{
93 int fd = open_trunc(name);
94 if (fd < 0)
95 bb_perror_msg("%s: warning: cannot open %s",
96 dir, name);
97 return fd;
98}
99
100static int rename_or_warn(const char *old, const char *new)
101{
102 if (rename(old, new) == -1) {
103 bb_perror_msg("%s: warning: cannot rename %s to %s",
104 dir, old, new);
105 return -1;
106 }
107 return 0;
108}
109
110static void update_status(struct svdir *s)
111{
112 unsigned long l;
113 int fd;
114 char status[20];
115
116 /* pid */
117 if (pidchanged) {
118 fd = open_trunc_or_warn("supervise/pid.new");
119 if (fd < 0)
120 return;
121 if (s->pid) {
122 char spid[sizeof(s->pid)*3 + 2];
123 int size = sprintf(spid, "%d\n", s->pid);
124 write(fd, spid, size);
125 }
126 close(fd);
127 if (s->islog) {
128 if (rename_or_warn("supervise/pid.new", "log/supervise/pid"))
129 return;
130 } else if (rename_or_warn("supervise/pid.new", "supervise/pid")) {
131 return;
132 }
133 pidchanged = 0;
134 }
135
136 /* stat */
137 fd = open_trunc_or_warn("supervise/stat.new");
138 if (fd < -1)
139 return;
140
141 {
142 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
143 char *p = stat_buf;
144 switch (s->state) {
145 case S_DOWN:
146 p = add_str(p, "down");
147 break;
148 case S_RUN:
149 p = add_str(p, "run");
150 break;
151 case S_FINISH:
152 p = add_str(p, "finish");
153 break;
154 }
155 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
156 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
157 if (s->state != S_DOWN)
158 switch(s->want) {
159 case W_DOWN:
160 p = add_str(p, ", want down");
161 break;
162 case W_EXIT:
163 p = add_str(p, ", want exit");
164 break;
165 }
166 *p++ = '\n';
167 write(fd, stat_buf, p - stat_buf);
168 close(fd);
169 }
170
171 if (s->islog) {
172 rename_or_warn("supervise/stat.new", "log/supervise/stat");
173 } else {
174 rename_or_warn("supervise/stat.new", "log/supervise/stat"+4);
175 }
176
177 /* supervise compatibility */
178 taia_pack(status, &s->start);
179 l = (unsigned long)s->pid;
180 status[12] = l; l >>=8;
181 status[13] = l; l >>=8;
182 status[14] = l; l >>=8;
183 status[15] = l;
184 if (s->ctrl & C_PAUSE)
185 status[16] = 1;
186 else
187 status[16] = 0;
188 if (s->want == W_UP)
189 status[17] = 'u';
190 else
191 status[17] = 'd';
192 if (s->ctrl & C_TERM)
193 status[18] = 1;
194 else
195 status[18] = 0;
196 status[19] = s->state;
197 fd = open_trunc_or_warn("supervise/status.new");
198 if (fd < 0)
199 return;
200 l = write(fd, status, sizeof status);
201 if (l < 0) {
202 warn_cannot("write supervise/status.new");
203 close(fd);
204 unlink("supervise/status.new");
205 return;
206 }
207 close(fd);
208 if (l < sizeof status) {
209 warnx_cannot("write supervise/status.new: partial write");
210 return;
211 }
212 if (s->islog) {
213 rename_or_warn("supervise/status.new", "log/supervise/status");
214 } else {
215 rename_or_warn("supervise/status.new", "log/supervise/status"+4);
216 }
217}
218
219static unsigned custom(struct svdir *s, char c)
220{
221 int pid;
222 int w;
223 char a[10];
224 struct stat st;
225 char *prog[2];
226
227 if (s->islog) return 0;
228 memcpy(a, "control/?", 10);
229 a[8] = c;
230 if (stat(a, &st) == 0) {
231 if (st.st_mode & S_IXUSR) {
232 pid = fork();
233 if (pid == -1) {
234 warn_cannot("fork for control/?");
235 return 0;
236 }
237 if (!pid) {
238 if (haslog && fd_copy(1, logpipe[1]) == -1)
239 warn_cannot("setup stdout for control/?");
240 prog[0] = a;
241 prog[1] = 0;
242 execve(a, prog, environ);
243 fatal_cannot("run control/?");
244 }
245 while (wait_pid(&w, pid) == -1) {
246 if (errno == EINTR) continue;
247 warn_cannot("wait for child control/?");
248 return 0;
249 }
250 return !wait_exitcode(w);
251 }
252 }
253 else {
254 if (errno == ENOENT) return 0;
255 warn_cannot("stat control/?");
256 }
257 return 0;
258}
259
260static void stopservice(struct svdir *s)
261{
262 if (s->pid && ! custom(s, 't')) {
263 kill(s->pid, SIGTERM);
264 s->ctrl |=C_TERM;
265 update_status(s);
266 }
267 if (s->want == W_DOWN) {
268 kill(s->pid, SIGCONT);
269 custom(s, 'd'); return;
270 }
271 if (s->want == W_EXIT) {
272 kill(s->pid, SIGCONT);
273 custom(s, 'x');
274 }
275}
276
277static void startservice(struct svdir *s)
278{
279 int p;
280 char *run[2];
281
282 if (s->state == S_FINISH)
283 run[0] = "./finish";
284 else {
285 run[0] = "./run";
286 custom(s, 'u');
287 }
288 run[1] = 0;
289
290 if (s->pid != 0) stopservice(s); /* should never happen */
291 while ((p = fork()) == -1) {
292 warn_cannot("fork, sleeping");
293 sleep(5);
294 }
295 if (p == 0) {
296 /* child */
297 if (haslog) {
298 if (s->islog) {
299 if (fd_copy(0, logpipe[0]) == -1)
300 fatal_cannot("setup filedescriptor for ./log/run");
301 close(logpipe[1]);
302 if (chdir("./log") == -1)
303 fatal_cannot("change directory to ./log");
304 } else {
305 if (fd_copy(1, logpipe[1]) == -1)
306 fatal_cannot("setup filedescriptor for ./run");
307 close(logpipe[0]);
308 }
309 }
310 sig_uncatch(sig_child);
311 sig_unblock(sig_child);
312 sig_uncatch(sig_term);
313 sig_unblock(sig_term);
314 execve(*run, run, environ);
315 if (s->islog)
316 fatal2_cannot("start log/", *run);
317 else
318 fatal2_cannot("start ", *run);
319 }
320 if (s->state != S_FINISH) {
321 taia_now(&s->start);
322 s->state = S_RUN;
323 }
324 s->pid = p;
325 pidchanged = 1;
326 s->ctrl = C_NOOP;
327 update_status(s);
328}
329
330static int ctrl(struct svdir *s, char c)
331{
332 switch(c) {
333 case 'd': /* down */
334 s->want = W_DOWN;
335 update_status(s);
336 if (s->pid && s->state != S_FINISH) stopservice(s);
337 break;
338 case 'u': /* up */
339 s->want = W_UP;
340 update_status(s);
341 if (s->pid == 0) startservice(s);
342 break;
343 case 'x': /* exit */
344 if (s->islog) break;
345 s->want = W_EXIT;
346 update_status(s);
347 if (s->pid && s->state != S_FINISH) stopservice(s);
348 break;
349 case 't': /* sig term */
350 if (s->pid && s->state != S_FINISH) stopservice(s);
351 break;
352 case 'k': /* sig kill */
353 if (s->pid && ! custom(s, c)) kill(s->pid, SIGKILL);
354 s->state = S_DOWN;
355 break;
356 case 'p': /* sig pause */
357 if (s->pid && ! custom(s, c)) kill(s->pid, SIGSTOP);
358 s->ctrl |=C_PAUSE;
359 update_status(s);
360 break;
361 case 'c': /* sig cont */
362 if (s->pid && ! custom(s, c)) kill(s->pid, SIGCONT);
363 if (s->ctrl & C_PAUSE) s->ctrl &=~C_PAUSE;
364 update_status(s);
365 break;
366 case 'o': /* once */
367 s->want = W_DOWN;
368 update_status(s);
369 if (!s->pid) startservice(s);
370 break;
371 case 'a': /* sig alarm */
372 if (s->pid && ! custom(s, c)) kill(s->pid, SIGALRM);
373 break;
374 case 'h': /* sig hup */
375 if (s->pid && ! custom(s, c)) kill(s->pid, SIGHUP);
376 break;
377 case 'i': /* sig int */
378 if (s->pid && ! custom(s, c)) kill(s->pid, SIGINT);
379 break;
380 case 'q': /* sig quit */
381 if (s->pid && ! custom(s, c)) kill(s->pid, SIGQUIT);
382 break;
383 case '1': /* sig usr1 */
384 if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR1);
385 break;
386 case '2': /* sig usr2 */
387 if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR2);
388 break;
389 }
390 return 1;
391}
392
393int runsv_main(int argc, char **argv)
394{
395 struct stat s;
396 int fd;
397 int r;
398 char buf[256];
399
400 if (!argv[1] || argv[2]) usage();
401 dir = argv[1];
402
403 if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe");
404 coe(selfpipe[0]);
405 coe(selfpipe[1]);
406 ndelay_on(selfpipe[0]);
407 ndelay_on(selfpipe[1]);
408
409 sig_block(sig_child);
410 sig_catch(sig_child, s_child);
411 sig_block(sig_term);
412 sig_catch(sig_term, s_term);
413
414 xchdir(dir);
415 svd[0].pid = 0;
416 svd[0].state = S_DOWN;
417 svd[0].ctrl = C_NOOP;
418 svd[0].want = W_UP;
419 svd[0].islog = 0;
420 svd[1].pid = 0;
421 taia_now(&svd[0].start);
422 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
423
424 if (stat("log", &s) == -1) {
425 if (errno != ENOENT)
426 warn_cannot("stat ./log");
427 } else {
428 if (!S_ISDIR(s.st_mode))
429 warnx_cannot("stat log/down: log is not a directory");
430 else {
431 haslog = 1;
432 svd[1].state = S_DOWN;
433 svd[1].ctrl = C_NOOP;
434 svd[1].want = W_UP;
435 svd[1].islog = 1;
436 taia_now(&svd[1].start);
437 if (stat("log/down", &s) != -1)
438 svd[1].want = W_DOWN;
439 if (pipe(logpipe) == -1)
440 fatal_cannot("create log pipe");
441 coe(logpipe[0]);
442 coe(logpipe[1]);
443 }
444 }
445
446 if (mkdir("supervise", 0700) == -1) {
447 r = readlink("supervise", buf, 256);
448 if (r != -1) {
449 if (r == 256)
450 fatal2x_cannot("readlink ./supervise: ", "name too long");
451 buf[r] = 0;
452 mkdir(buf, 0700);
453 } else {
454 if ((errno != ENOENT) && (errno != EINVAL))
455 fatal_cannot("readlink ./supervise");
456 }
457 }
458 svd[0].fdlock = xopen3("log/supervise/lock"+4,
459 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
460 if (lock_exnb(svd[0].fdlock) == -1)
461 fatal_cannot("lock supervise/lock");
462 coe(svd[0].fdlock);
463 if (haslog) {
464 if (mkdir("log/supervise", 0700) == -1) {
465 r = readlink("log/supervise", buf, 256);
466 if (r != -1) {
467 if (r == 256)
468 fatal2x_cannot("readlink ./log/supervise: ", "name too long");
469 buf[r] = 0;
470 fd = xopen(".", O_RDONLY|O_NDELAY);
471 xchdir("./log");
472 mkdir(buf, 0700);
473 if (fchdir(fd) == -1)
474 fatal_cannot("change back to service directory");
475 close(fd);
476 }
477 else {
478 if ((errno != ENOENT) && (errno != EINVAL))
479 fatal_cannot("readlink ./log/supervise");
480 }
481 }
482 svd[1].fdlock = xopen3("log/supervise/lock",
483 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
484 if (lock_ex(svd[1].fdlock) == -1)
485 fatal_cannot("lock log/supervise/lock");
486 coe(svd[1].fdlock);
487 }
488
489 fifo_make("log/supervise/control"+4, 0600);
490 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
491 coe(svd[0].fdcontrol);
492 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
493 coe(svd[0].fdcontrolwrite);
494 update_status(&svd[0]);
495 if (haslog) {
496 fifo_make("log/supervise/control", 0600);
497 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
498 coe(svd[1].fdcontrol);
499 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
500 coe(svd[1].fdcontrolwrite);
501 update_status(&svd[1]);
502 }
503 fifo_make("log/supervise/ok"+4, 0600);
504 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
505 coe(fd);
506 if (haslog) {
507 fifo_make("log/supervise/ok", 0600);
508 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
509 coe(fd);
510 }
511 for (;;) {
512 iopause_fd x[3];
513 struct taia deadline;
514 struct taia now;
515 char ch;
516
517 if (haslog)
518 if (!svd[1].pid && svd[1].want == W_UP)
519 startservice(&svd[1]);
520 if (!svd[0].pid)
521 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
522 startservice(&svd[0]);
523
524 x[0].fd = selfpipe[0];
525 x[0].events = IOPAUSE_READ;
526 x[1].fd = svd[0].fdcontrol;
527 x[1].events = IOPAUSE_READ;
528 if (haslog) {
529 x[2].fd = svd[1].fdcontrol;
530 x[2].events = IOPAUSE_READ;
531 }
532 taia_now(&now);
533 taia_uint(&deadline, 3600);
534 taia_add(&deadline, &now, &deadline);
535
536 sig_unblock(sig_term);
537 sig_unblock(sig_child);
538 iopause(x, 2+haslog, &deadline, &now);
539 sig_block(sig_term);
540 sig_block(sig_child);
541
542 while (read(selfpipe[0], &ch, 1) == 1)
543 ;
544 for (;;) {
545 int child;
546 int wstat;
547
548 child = wait_nohang(&wstat);
549 if (!child) break;
550 if ((child == -1) && (errno != EINTR)) break;
551 if (child == svd[0].pid) {
552 svd[0].pid = 0;
553 pidchanged = 1;
554 svd[0].ctrl &=~C_TERM;
555 if (svd[0].state != S_FINISH)
556 fd = open_read("finish");
557 if (fd != -1) {
558 close(fd);
559 svd[0].state = S_FINISH;
560 update_status(&svd[0]);
561 continue;
562 }
563 svd[0].state = S_DOWN;
564 taia_uint(&deadline, 1);
565 taia_add(&deadline, &svd[0].start, &deadline);
566 taia_now(&svd[0].start);
567 update_status(&svd[0]);
568 if (taia_less(&svd[0].start, &deadline)) sleep(1);
569 }
570 if (haslog) {
571 if (child == svd[1].pid) {
572 svd[1].pid = 0;
573 pidchanged = 1;
574 svd[1].state = S_DOWN;
575 svd[1].ctrl &=~C_TERM;
576 taia_uint(&deadline, 1);
577 taia_add(&deadline, &svd[1].start, &deadline);
578 taia_now(&svd[1].start);
579 update_status(&svd[1]);
580 if (taia_less(&svd[1].start, &deadline)) sleep(1);
581 }
582 }
583 }
584 if (read(svd[0].fdcontrol, &ch, 1) == 1)
585 ctrl(&svd[0], ch);
586 if (haslog)
587 if (read(svd[1].fdcontrol, &ch, 1) == 1)
588 ctrl(&svd[1], ch);
589
590 if (sigterm) {
591 ctrl(&svd[0], 'x');
592 sigterm = 0;
593 }
594
595 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
596 if (svd[1].pid == 0)
597 _exit(0);
598 if (svd[1].want != W_EXIT) {
599 svd[1].want = W_EXIT;
600 /* stopservice(&svd[1]); */
601 update_status(&svd[1]);
602 close(logpipe[1]);
603 close(logpipe[0]);
604 //if (close(logpipe[1]) == -1)
605 // warn_cannot("close logpipe[1]");
606 //if (close(logpipe[0]) == -1)
607 // warn_cannot("close logpipe[0]");
608 }
609 }
610 }
611 /* not reached */
612 return 0;
613}
diff --git a/runit/runsvdir.c b/runit/runsvdir.c
new file mode 100644
index 000000000..9238eec82
--- /dev/null
+++ b/runit/runsvdir.c
@@ -0,0 +1,306 @@
1/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
9#define MAXSERVICES 1000
10
11static char *svdir;
12static unsigned long dev;
13static unsigned long ino;
14static struct service {
15 unsigned long dev;
16 unsigned long ino;
17 int pid;
18 int isgone;
19} *sv;
20static int svnum;
21static int check = 1;
22static char *rplog;
23static int rploglen;
24static int logpipe[2];
25static iopause_fd io[1];
26static struct taia stamplog;
27static int exitsoon;
28static int pgrp;
29
30#define usage() bb_show_usage()
31static void fatal2_cannot(char *m1, char *m2)
32{
33 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
34 /* was exiting 100 */
35}
36static void warn3x(char *m1, char *m2, char *m3)
37{
38 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
39}
40static void warn2_cannot(char *m1, char *m2)
41{
42 warn3x("cannot ", m1, m2);
43}
44static void warnx(char *m1)
45{
46 warn3x(m1, "", "");
47}
48
49static void s_term(int sig_no)
50{
51 exitsoon = 1;
52}
53static void s_hangup(int sig_no)
54{
55 exitsoon = 2;
56}
57
58static void runsv(int no, char *name)
59{
60 int pid = fork();
61
62 if (pid == -1) {
63 warn2_cannot("fork for ", name);
64 return;
65 }
66 if (pid == 0) {
67 /* child */
68 char *prog[3];
69
70 prog[0] = "runsv";
71 prog[1] = name;
72 prog[2] = 0;
73 sig_uncatch(sig_hangup);
74 sig_uncatch(sig_term);
75 if (pgrp) setsid();
76 execvp(prog[0], prog);
77 //pathexec_run(*prog, prog, (char* const*)environ);
78 fatal2_cannot("start runsv ", name);
79 }
80 sv[no].pid = pid;
81}
82
83static void runsvdir(void)
84{
85 DIR *dir;
86 direntry *d;
87 int i;
88 struct stat s;
89
90 dir = opendir(".");
91 if (!dir) {
92 warn2_cannot("open directory ", svdir);
93 return;
94 }
95 for (i = 0; i < svnum; i++)
96 sv[i].isgone = 1;
97 errno = 0;
98 while ((d = readdir(dir))) {
99 if (d->d_name[0] == '.') continue;
100 if (stat(d->d_name, &s) == -1) {
101 warn2_cannot("stat ", d->d_name);
102 errno = 0;
103 continue;
104 }
105 if (!S_ISDIR(s.st_mode)) continue;
106 for (i = 0; i < svnum; i++) {
107 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
108 sv[i].isgone = 0;
109 if (!sv[i].pid)
110 runsv(i, d->d_name);
111 break;
112 }
113 }
114 if (i == svnum) {
115 /* new service */
116 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
117 if (!svnew) {
118 warn3x("cannot start runsv ", d->d_name,
119 " too many services");
120 continue;
121 }
122 sv = svnew;
123 svnum++;
124 memset(&sv[i], 0, sizeof(sv[i]));
125 sv[i].ino = s.st_ino;
126 sv[i].dev = s.st_dev;
127 //sv[i].pid = 0;
128 //sv[i].isgone = 0;
129 runsv(i, d->d_name);
130 check = 1;
131 }
132 }
133 if (errno) {
134 warn2_cannot("read directory ", svdir);
135 closedir(dir);
136 check = 1;
137 return;
138 }
139 closedir(dir);
140
141 /* SIGTERM removed runsv's */
142 for (i = 0; i < svnum; i++) {
143 if (!sv[i].isgone)
144 continue;
145 if (sv[i].pid)
146 kill(sv[i].pid, SIGTERM);
147 sv[i] = sv[--svnum];
148 check = 1;
149 }
150}
151
152static int setup_log(void)
153{
154 rploglen = strlen(rplog);
155 if (rploglen < 7) {
156 warnx("log must have at least seven characters");
157 return 0;
158 }
159 if (pipe(logpipe) == -1) {
160 warnx("cannot create pipe for log");
161 return -1;
162 }
163 coe(logpipe[1]);
164 coe(logpipe[0]);
165 ndelay_on(logpipe[0]);
166 ndelay_on(logpipe[1]);
167 if (fd_copy(2, logpipe[1]) == -1) {
168 warnx("cannot set filedescriptor for log");
169 return -1;
170 }
171 io[0].fd = logpipe[0];
172 io[0].events = IOPAUSE_READ;
173 taia_now(&stamplog);
174 return 1;
175}
176
177int runsvdir_main(int argc, char **argv)
178{
179 struct stat s;
180 time_t mtime = 0;
181 int wstat;
182 int curdir;
183 int pid;
184 struct taia deadline;
185 struct taia now;
186 struct taia stampcheck;
187 char ch;
188 int i;
189
190 argv++;
191 if (!argv || !*argv) usage();
192 if (**argv == '-') {
193 switch (*(*argv + 1)) {
194 case 'P': pgrp = 1;
195 case '-': ++argv;
196 }
197 if (!argv || !*argv) usage();
198 }
199
200 sig_catch(sig_term, s_term);
201 sig_catch(sig_hangup, s_hangup);
202 svdir = *argv++;
203 if (argv && *argv) {
204 rplog = *argv;
205 if (setup_log() != 1) {
206 rplog = 0;
207 warnx("log service disabled");
208 }
209 }
210 curdir = open_read(".");
211 if (curdir == -1)
212 fatal2_cannot("open current directory", "");
213 coe(curdir);
214
215 taia_now(&stampcheck);
216
217 for (;;) {
218 /* collect children */
219 for (;;) {
220 pid = wait_nohang(&wstat);
221 if (pid <= 0) break;
222 for (i = 0; i < svnum; i++) {
223 if (pid == sv[i].pid) {
224 /* runsv has gone */
225 sv[i].pid = 0;
226 check = 1;
227 break;
228 }
229 }
230 }
231
232 taia_now(&now);
233 if (now.sec.x < (stampcheck.sec.x - 3)) {
234 /* time warp */
235 warnx("time warp: resetting time stamp");
236 taia_now(&stampcheck);
237 taia_now(&now);
238 if (rplog) taia_now(&stamplog);
239 }
240 if (taia_less(&now, &stampcheck) == 0) {
241 /* wait at least a second */
242 taia_uint(&deadline, 1);
243 taia_add(&stampcheck, &now, &deadline);
244
245 if (stat(svdir, &s) != -1) {
246 if (check || s.st_mtime != mtime
247 || s.st_ino != ino || s.st_dev != dev
248 ) {
249 /* svdir modified */
250 if (chdir(svdir) != -1) {
251 mtime = s.st_mtime;
252 dev = s.st_dev;
253 ino = s.st_ino;
254 check = 0;
255 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
256 sleep(1);
257 runsvdir();
258 while (fchdir(curdir) == -1) {
259 warn2_cannot("change directory, pausing", "");
260 sleep(5);
261 }
262 } else
263 warn2_cannot("change directory to ", svdir);
264 }
265 } else
266 warn2_cannot("stat ", svdir);
267 }
268
269 if (rplog) {
270 if (taia_less(&now, &stamplog) == 0) {
271 write(logpipe[1], ".", 1);
272 taia_uint(&deadline, 900);
273 taia_add(&stamplog, &now, &deadline);
274 }
275 }
276 taia_uint(&deadline, check ? 1 : 5);
277 taia_add(&deadline, &now, &deadline);
278
279 sig_block(sig_child);
280 if (rplog)
281 iopause(io, 1, &deadline, &now);
282 else
283 iopause(0, 0, &deadline, &now);
284 sig_unblock(sig_child);
285
286 if (rplog && (io[0].revents | IOPAUSE_READ))
287 while (read(logpipe[0], &ch, 1) > 0)
288 if (ch) {
289 for (i = 6; i < rploglen; i++)
290 rplog[i-1] = rplog[i];
291 rplog[rploglen-1] = ch;
292 }
293
294 switch (exitsoon) {
295 case 1:
296 _exit(0);
297 case 2:
298 for (i = 0; i < svnum; i++)
299 if (sv[i].pid)
300 kill(sv[i].pid, SIGTERM);
301 _exit(111);
302 }
303 }
304 /* not reached */
305 return 0;
306}
diff --git a/runit/sv.c b/runit/sv.c
new file mode 100644
index 000000000..819f31419
--- /dev/null
+++ b/runit/sv.c
@@ -0,0 +1,360 @@
1/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
9static char *action;
10static char *acts;
11static char *varservice = "/var/service/";
12static char **service;
13static char **servicex;
14static unsigned services;
15static unsigned rc = 0;
16static unsigned verbose = 0;
17static unsigned long waitsec = 7;
18static unsigned kll = 0;
19static struct taia tstart, tnow, tdiff;
20static struct tai tstatus;
21
22static int (*act)(char*) = 0;
23static int (*cbk)(char*) = 0;
24
25static int curdir, fd, r;
26static char svstatus[20];
27
28#define usage() bb_show_usage()
29
30static void fatal_cannot(char *m1)
31{
32 bb_perror_msg("fatal: cannot %s", m1);
33 _exit(151);
34}
35
36static void out(char *p, char *m1)
37{
38 printf("%s%s: %s", p, *service, m1);
39 if (errno) {
40 printf(": %s", strerror(errno));
41 }
42 puts(""); /* will also flush the output */
43}
44
45#define FAIL "fail: "
46#define WARN "warning: "
47#define OK "ok: "
48#define RUN "run: "
49#define FINISH "finish: "
50#define DOWN "down: "
51#define TIMEOUT "timeout: "
52#define KILL "kill: "
53
54static void fail(char *m1) { ++rc; out(FAIL, m1); }
55static void failx(char *m1) { errno = 0; fail(m1); }
56static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); }
57static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }
58static void ok(char *m1) { errno = 0; out(OK, m1); }
59
60static int svstatus_get(void)
61{
62 if ((fd = open_write("supervise/ok")) == -1) {
63 if (errno == ENODEV) {
64 *acts == 'x' ? ok("runsv not running")
65 : failx("runsv not running");
66 return 0;
67 }
68 warn_cannot("open supervise/ok");
69 return -1;
70 }
71 close(fd);
72 if ((fd = open_read("supervise/status")) == -1) {
73 warn_cannot("open supervise/status");
74 return -1;
75 }
76 r = read(fd, svstatus, 20);
77 close(fd);
78 switch(r) {
79 case 20: break;
80 case -1: warn_cannot("read supervise/status"); return -1;
81 default: warnx_cannot("read supervise/status: bad format"); return -1;
82 }
83 return 1;
84}
85
86static unsigned svstatus_print(char *m)
87{
88 int pid;
89 int normallyup = 0;
90 struct stat s;
91
92 if (stat("down", &s) == -1) {
93 if (errno != ENOENT) {
94 bb_perror_msg(WARN"cannot stat %s/down", *service);
95 return 0;
96 }
97 normallyup = 1;
98 }
99 pid = (unsigned char) svstatus[15];
100 pid <<= 8; pid += (unsigned char)svstatus[14];
101 pid <<= 8; pid += (unsigned char)svstatus[13];
102 pid <<= 8; pid += (unsigned char)svstatus[12];
103 tai_unpack(svstatus, &tstatus);
104 if (pid) {
105 switch (svstatus[19]) {
106 case 1: printf(RUN); break;
107 case 2: printf(FINISH); break;
108 }
109 printf("%s: (pid %d) ", m, pid);
110 }
111 else {
112 printf(DOWN"%s: ", m);
113 }
114 printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x));
115 if (pid && !normallyup) printf(", normally down");
116 if (!pid && normallyup) printf(", normally up");
117 if (pid && svstatus[16]) printf(", paused");
118 if (!pid && (svstatus[17] == 'u')) printf(", want up");
119 if (pid && (svstatus[17] == 'd')) printf(", want down");
120 if (pid && svstatus[18]) printf(", got TERM");
121 return pid ? 1 : 2;
122}
123
124static int status(char *unused)
125{
126 r = svstatus_get();
127 switch(r) { case -1: case 0: return 0; }
128 r = svstatus_print(*service);
129 if (chdir("log") == -1) {
130 if (errno != ENOENT) {
131 printf("; log: "WARN"cannot change to log service directory: %s",
132 strerror(errno));
133 }
134 } else if (svstatus_get()) {
135 printf("; ");
136 svstatus_print("log");
137 }
138 puts(""); /* will also flush the output */
139 return r;
140}
141
142static int checkscript(void)
143{
144 char *prog[2];
145 struct stat s;
146 int pid, w;
147
148 if (stat("check", &s) == -1) {
149 if (errno == ENOENT) return 1;
150 bb_perror_msg(WARN"cannot stat %s/check", *service);
151 return 0;
152 }
153 /* if (!(s.st_mode & S_IXUSR)) return 1; */
154 if ((pid = fork()) == -1) {
155 bb_perror_msg(WARN"cannot fork for %s/check", *service);
156 return 0;
157 }
158 if (!pid) {
159 prog[0] = "./check";
160 prog[1] = 0;
161 close(1);
162 execve("check", prog, environ);
163 bb_perror_msg(WARN"cannot run %s/check", *service);
164 _exit(0);
165 }
166 while (wait_pid(&w, pid) == -1) {
167 if (errno == EINTR) continue;
168 bb_perror_msg(WARN"cannot wait for child %s/check", *service);
169 return 0;
170 }
171 return !wait_exitcode(w);
172}
173
174static int check(char *a)
175{
176 unsigned pid;
177
178 if ((r = svstatus_get()) == -1) return -1;
179 if (r == 0) { if (*a == 'x') return 1; return -1; }
180 pid = (unsigned char)svstatus[15];
181 pid <<= 8; pid += (unsigned char)svstatus[14];
182 pid <<= 8; pid += (unsigned char)svstatus[13];
183 pid <<= 8; pid += (unsigned char)svstatus[12];
184 switch (*a) {
185 case 'x': return 0;
186 case 'u':
187 if (!pid || svstatus[19] != 1) return 0;
188 if (!checkscript()) return 0;
189 break;
190 case 'd': if (pid) return 0; break;
191 case 'c': if (pid) if (!checkscript()) return 0; break;
192 case 't':
193 if (!pid && svstatus[17] == 'd') break;
194 tai_unpack(svstatus, &tstatus);
195 if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
196 return 0;
197 break;
198 case 'o':
199 tai_unpack(svstatus, &tstatus);
200 if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
201 return 0;
202 }
203 printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */
204 return 1;
205}
206
207static int control(char *a)
208{
209 if (svstatus_get() <= 0) return -1;
210 if (svstatus[17] == *a) return 0;
211 if ((fd = open_write("supervise/control")) == -1) {
212 if (errno != ENODEV)
213 warn_cannot("open supervise/control");
214 else
215 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
216 return -1;
217 }
218 r = write(fd, a, strlen(a));
219 close(fd);
220 if (r != strlen(a)) {
221 warn_cannot("write to supervise/control");
222 return -1;
223 }
224 return 1;
225}
226
227int sv_main(int argc, char **argv)
228{
229 unsigned opt;
230 unsigned i, want_exit;
231 char *x;
232
233 for (i = strlen(*argv); i; --i)
234 if ((*argv)[i-1] == '/')
235 break;
236 *argv += i;
237 service = argv;
238 services = 1;
239 if ((x = getenv("SVDIR"))) varservice = x;
240 if ((x = getenv("SVWAIT"))) waitsec = xatoul(x);
241 /* TODO: V can be handled internally by getopt_ulflags */
242 opt = getopt32(argc, argv, "w:vV", &x);
243 if (opt & 1) waitsec = xatoul(x);
244 if (opt & 2) verbose = 1;
245 if (opt & 4) usage();
246 if (!(action = *argv++)) usage();
247 --argc;
248 service = argv; services = argc;
249 if (!*service) usage();
250
251 taia_now(&tnow); tstart = tnow;
252 if ((curdir = open_read(".")) == -1)
253 fatal_cannot("open current directory");
254
255 act = &control; acts = "s";
256 if (verbose) cbk = &check;
257 switch (*action) {
258 case 'x': case 'e':
259 acts = "x"; break;
260 case 'X': case 'E':
261 acts = "x"; kll = 1; cbk = &check; break;
262 case 'D':
263 acts = "d"; kll = 1; cbk = &check; break;
264 case 'T':
265 acts = "tc"; kll = 1; cbk = &check; break;
266 case 'c':
267 if (!str_diff(action, "check")) {
268 act = 0;
269 acts = "c";
270 cbk = &check;
271 break;
272 }
273 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
274 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
275 action[1] = 0; acts = action; break;
276 case 's':
277 if (!str_diff(action, "shutdown")) {
278 acts = "x";
279 cbk = &check;
280 break;
281 }
282 if (!str_diff(action, "start")) {
283 acts = "u";
284 cbk = &check;
285 break;
286 }
287 if (!str_diff(action, "stop")) {
288 acts = "d";
289 cbk = &check;
290 break;
291 }
292 act = &status; cbk = 0; break;
293 case 'r':
294 if (!str_diff(action, "restart")) {
295 acts = "tcu";
296 cbk = &check;
297 break;
298 }
299 usage();
300 case 'f':
301 if (!str_diff(action, "force-reload"))
302 { acts = "tc"; kll = 1; cbk = &check; break; }
303 if (!str_diff(action, "force-restart"))
304 { acts = "tcu"; kll = 1; cbk = &check; break; }
305 if (!str_diff(action, "force-shutdown"))
306 { acts = "x"; kll = 1; cbk = &check; break; }
307 if (!str_diff(action, "force-stop"))
308 { acts = "d"; kll = 1; cbk = &check; break; }
309 default:
310 usage();
311 }
312
313 servicex = service;
314 for (i = 0; i < services; ++i) {
315 if ((**service != '/') && (**service != '.')) {
316 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
317 fail("cannot change to service directory");
318 *service = 0;
319 }
320 } else if (chdir(*service) == -1) {
321 fail("cannot change to service directory");
322 *service = 0;
323 }
324 if (*service) if (act && (act(acts) == -1)) *service = 0;
325 if (fchdir(curdir) == -1) fatal_cannot("change to original directory");
326 service++;
327 }
328
329 if (*cbk)
330 for (;;) {
331 taia_sub(&tdiff, &tnow, &tstart);
332 service = servicex; want_exit = 1;
333 for (i = 0; i < services; ++i, ++service) {
334 if (!*service) continue;
335 if ((**service != '/') && (**service != '.')) {
336 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
337 fail("cannot change to service directory");
338 *service = 0;
339 }
340 } else if (chdir(*service) == -1) {
341 fail("cannot change to service directory");
342 *service = 0;
343 }
344 if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; }
345 if (*service && taia_approx(&tdiff) > waitsec) {
346 kll ? printf(KILL) : printf(TIMEOUT);
347 if (svstatus_get() > 0) { svstatus_print(*service); ++rc; }
348 puts(""); /* will also flush the output */
349 if (kll) control("k");
350 *service = 0;
351 }
352 if (fchdir(curdir) == -1)
353 fatal_cannot("change to original directory");
354 }
355 if (want_exit) break;
356 usleep(420000);
357 taia_now(&tnow);
358 }
359 return rc > 99 ? 99 : rc;
360}
diff --git a/runit/svlogd.c b/runit/svlogd.c
new file mode 100644
index 000000000..7024c3db4
--- /dev/null
+++ b/runit/svlogd.c
@@ -0,0 +1,878 @@
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
33#include "busybox.h"
34#include "runit_lib.h"
35
36static unsigned verbose;
37static int linemax = 1000;
38static int buflen = 1024;
39static int linelen;
40
41static char **fndir;
42static int fdwdir;
43static int wstat;
44static struct taia trotate;
45
46static char *line;
47static unsigned exitasap;
48static unsigned rotateasap;
49static unsigned reopenasap;
50static unsigned linecomplete = 1;
51static unsigned tmaxflag;
52static iopause_fd in;
53
54static const char *replace = "";
55static char repl;
56
57static struct logdir {
58 char *btmp;
59 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
60 char *inst;
61 char *processor;
62 char *name;
63 unsigned size;
64 unsigned sizemax;
65 unsigned nmax;
66 unsigned nmin;
67 /* int (not long) because of taia_uint() usage: */
68 unsigned tmax;
69 int ppid;
70 int fddir;
71 int fdcur;
72 int fdlock;
73 struct taia trotate;
74 char fnsave[FMT_PTIME];
75 char match;
76 char matcherr;
77} *dir;
78static unsigned dirn = 0;
79
80#define FATAL "fatal: "
81#define WARNING "warning: "
82#define PAUSE "pausing: "
83#define INFO "info: "
84
85#define usage() bb_show_usage()
86static void fatalx(char *m0)
87{
88 bb_error_msg_and_die(FATAL"%s", m0);
89}
90static void warn(char *m0) {
91 bb_perror_msg(WARNING"%s", m0);
92}
93static void warn2(char *m0, char *m1)
94{
95 bb_perror_msg(WARNING"%s: %s", m0, m1);
96}
97static void warnx(char *m0, char *m1)
98{
99 bb_error_msg(WARNING"%s: %s", m0, m1);
100}
101static void pause_nomem(void)
102{
103 bb_error_msg(PAUSE"out of memory"); sleep(3);
104}
105static void pause1cannot(char *m0)
106{
107 bb_perror_msg(PAUSE"cannot %s", m0); sleep(3);
108}
109static void pause2cannot(char *m0, char *m1)
110{
111 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
112 sleep(3);
113}
114
115static char* wstrdup(const char *str)
116{
117 char *s;
118 while (!(s = strdup(str))) pause_nomem();
119 return s;
120}
121
122static unsigned processorstart(struct logdir *ld)
123{
124 int pid;
125
126 if (!ld->processor) return 0;
127 if (ld->ppid) {
128 warnx("processor already running", ld->name);
129 return 0;
130 }
131 while ((pid = fork()) == -1)
132 pause2cannot("fork for processor", ld->name);
133 if (!pid) {
134 char *prog[4];
135 int fd;
136
137 /* child */
138 sig_uncatch(sig_term);
139 sig_uncatch(sig_alarm);
140 sig_uncatch(sig_hangup);
141 sig_unblock(sig_term);
142 sig_unblock(sig_alarm);
143 sig_unblock(sig_hangup);
144
145 if (verbose)
146 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
147 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
148 if (fd_move(0, fd) == -1)
149 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
150 ld->fnsave[26] = 't';
151 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
152 if (fd_move(1, fd) == -1)
153 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
154 fd = open_read("state");
155 if (fd == -1) {
156 if (errno != ENOENT)
157 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
158 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
159 fd = xopen("state", O_RDONLY|O_NDELAY);
160 }
161 if (fd_move(4, fd) == -1)
162 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
163 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
164 if (fd_move(5, fd) == -1)
165 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
166
167 prog[0] = "sh";
168 prog[1] = "-c";
169 prog[2] = ld->processor;
170 prog[3] = '\0';
171 execve("/bin/sh", prog, environ);
172 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
173 }
174 ld->ppid = pid;
175 return 1;
176}
177
178static unsigned processorstop(struct logdir *ld)
179{
180 char f[28];
181
182 if (ld->ppid) {
183 sig_unblock(sig_hangup);
184 while (wait_pid(&wstat, ld->ppid) == -1)
185 pause2cannot("wait for processor", ld->name);
186 sig_block(sig_hangup);
187 ld->ppid = 0;
188 }
189 if (ld->fddir == -1) return 1;
190 while (fchdir(ld->fddir) == -1)
191 pause2cannot("change directory, want processor", ld->name);
192 if (wait_exitcode(wstat) != 0) {
193 warnx("processor failed, restart", ld->name);
194 ld->fnsave[26] = 't';
195 unlink(ld->fnsave);
196 ld->fnsave[26] = 'u';
197 processorstart(ld);
198 while (fchdir(fdwdir) == -1)
199 pause1cannot("change to initial working directory");
200 return ld->processor ? 0 : 1;
201 }
202 ld->fnsave[26] = 't';
203 memcpy(f, ld->fnsave, 26);
204 f[26] = 's';
205 f[27] = '\0';
206 while (rename(ld->fnsave, f) == -1)
207 pause2cannot("rename processed", ld->name);
208 while (chmod(f, 0744) == -1)
209 pause2cannot("set mode of processed", ld->name);
210 ld->fnsave[26] = 'u';
211 if (unlink(ld->fnsave) == -1)
212 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
213 while (rename("newstate", "state") == -1)
214 pause2cannot("rename state", ld->name);
215 if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f);
216 while (fchdir(fdwdir) == -1)
217 pause1cannot("change to initial working directory");
218 return 1;
219}
220
221static void rmoldest(struct logdir *ld)
222{
223 DIR *d;
224 struct dirent *f;
225 char oldest[FMT_PTIME];
226 int n = 0;
227
228 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
229 while (!(d = opendir(".")))
230 pause2cannot("open directory, want rotate", ld->name);
231 errno = 0;
232 while ((f = readdir(d))) {
233 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
234 if (f->d_name[26] == 't') {
235 if (unlink(f->d_name) == -1)
236 warn2("cannot unlink processor leftover", f->d_name);
237 } else {
238 ++n;
239 if (strcmp(f->d_name, oldest) < 0)
240 memcpy(oldest, f->d_name, 27);
241 }
242 errno = 0;
243 }
244 }
245 if (errno) warn2("cannot read directory", ld->name);
246 closedir(d);
247
248 if (ld->nmax && (n > ld->nmax)) {
249 if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
250 if ((*oldest == '@') && (unlink(oldest) == -1))
251 warn2("cannot unlink oldest logfile", ld->name);
252 }
253}
254
255static unsigned rotate(struct logdir *ld)
256{
257 struct stat st;
258 struct taia now;
259
260 if (ld->fddir == -1) {
261 ld->tmax = 0;
262 return 0;
263 }
264 if (ld->ppid)
265 while(!processorstop(ld))
266 /* wait */;
267
268 while (fchdir(ld->fddir) == -1)
269 pause2cannot("change directory, want rotate", ld->name);
270
271 /* create new filename */
272 ld->fnsave[25] = '.';
273 ld->fnsave[26] = 's';
274 if (ld->processor)
275 ld->fnsave[26] = 'u';
276 ld->fnsave[27] = '\0';
277 do {
278 taia_now(&now);
279 fmt_taia(ld->fnsave, &now);
280 errno = 0;
281 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
282
283 if (ld->tmax && taia_less(&ld->trotate, &now)) {
284 taia_uint(&ld->trotate, ld->tmax);
285 taia_add(&ld->trotate, &now, &ld->trotate);
286 if (taia_less(&ld->trotate, &trotate))
287 trotate = ld->trotate;
288 }
289
290 if (ld->size > 0) {
291 while (fsync(ld->fdcur) == -1)
292 pause2cannot("fsync current logfile", ld->name);
293 while (fchmod(ld->fdcur, 0744) == -1)
294 pause2cannot("set mode of current", ld->name);
295 close(ld->fdcur);
296 if (verbose) {
297 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
298 ld->fnsave, ld->size);
299 }
300 while (rename("current", ld->fnsave) == -1)
301 pause2cannot("rename current", ld->name);
302 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
303 pause2cannot("create new current", ld->name);
304 coe(ld->fdcur);
305 ld->size = 0;
306 while (fchmod(ld->fdcur, 0644) == -1)
307 pause2cannot("set mode of current", ld->name);
308 rmoldest(ld);
309 processorstart(ld);
310 }
311
312 while (fchdir(fdwdir) == -1)
313 pause1cannot("change to initial working directory");
314 return 1;
315}
316
317static int buffer_pwrite(int n, char *s, unsigned len)
318{
319 int i;
320 struct logdir *ld = &dir[n];
321
322 if (ld->sizemax) {
323 if (ld->size >= ld->sizemax)
324 rotate(ld);
325 if (len > (ld->sizemax - ld->size))
326 len = ld->sizemax - ld->size;
327 }
328 while ((i = write(ld->fdcur, s, len)) == -1) {
329 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
330 DIR *d;
331 struct dirent *f;
332 char oldest[FMT_PTIME];
333 int j = 0;
334
335 while (fchdir(ld->fddir) == -1)
336 pause2cannot("change directory, want remove old logfile",
337 ld->name);
338 oldest[0] = 'A';
339 oldest[1] = oldest[27] = '\0';
340 while (!(d = opendir(".")))
341 pause2cannot("open directory, want remove old logfile",
342 ld->name);
343 errno = 0;
344 while ((f = readdir(d)))
345 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
346 ++j;
347 if (strcmp(f->d_name, oldest) < 0)
348 memcpy(oldest, f->d_name, 27);
349 }
350 if (errno) warn2("cannot read directory, want remove old logfile",
351 ld->name);
352 closedir(d);
353 errno = ENOSPC;
354 if (j > ld->nmin) {
355 if (*oldest == '@') {
356 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
357 ld->name, oldest);
358 errno = 0;
359 if (unlink(oldest) == -1) {
360 warn2("cannot unlink oldest logfile", ld->name);
361 errno = ENOSPC;
362 }
363 while (fchdir(fdwdir) == -1)
364 pause1cannot("change to initial working directory");
365 }
366 }
367 }
368 if (errno) pause2cannot("write to current", ld->name);
369 }
370
371 ld->size += i;
372 if (ld->sizemax)
373 if (s[i-1] == '\n')
374 if (ld->size >= (ld->sizemax - linemax))
375 rotate(ld);
376 return i;
377}
378
379static void logdir_close(struct logdir *ld)
380{
381 if (ld->fddir == -1)
382 return;
383 if (verbose)
384 bb_error_msg(INFO"close: %s", ld->name);
385 close(ld->fddir);
386 ld->fddir = -1;
387 if (ld->fdcur == -1)
388 return; /* impossible */
389 while (fsync(ld->fdcur) == -1)
390 pause2cannot("fsync current logfile", ld->name);
391 while (fchmod(ld->fdcur, 0744) == -1)
392 pause2cannot("set mode of current", ld->name);
393 close(ld->fdcur);
394 ld->fdcur = -1;
395 if (ld->fdlock == -1)
396 return; /* impossible */
397 close(ld->fdlock);
398 ld->fdlock = -1;
399 free(ld->processor);
400 ld->processor = NULL;
401}
402
403static unsigned logdir_open(struct logdir *ld, const char *fn)
404{
405 char buf[128];
406 struct taia now;
407 char *new, *s, *np;
408 int i;
409 struct stat st;
410
411 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
412 if (ld->fddir == -1) {
413 warn2("cannot open log directory", (char*)fn);
414 return 0;
415 }
416 coe(ld->fddir);
417 if (fchdir(ld->fddir) == -1) {
418 logdir_close(ld);
419 warn2("cannot change directory", (char*)fn);
420 return 0;
421 }
422 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
423 if ((ld->fdlock == -1)
424 || (lock_exnb(ld->fdlock) == -1)
425 ) {
426 logdir_close(ld);
427 warn2("cannot lock directory", (char*)fn);
428 while (fchdir(fdwdir) == -1)
429 pause1cannot("change to initial working directory");
430 return 0;
431 }
432 coe(ld->fdlock);
433
434 ld->size = 0;
435 ld->sizemax = 1000000;
436 ld->nmax = ld->nmin = 10;
437 ld->tmax = 0;
438 ld->name = (char*)fn;
439 ld->ppid = 0;
440 ld->match = '+';
441 free(ld->inst); ld->inst = NULL;
442 free(ld->processor); ld->processor = NULL;
443
444 /* read config */
445 i = open_read_close("config", buf, sizeof(buf));
446 if (i < 0)
447 warn2("cannot read config", ld->name);
448 if (i > 0) {
449 if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
450 s = buf;
451 while (s) {
452 np = strchr(s, '\n');
453 if (np) *np++ = '\0';
454 switch (s[0]) {
455 case '+':
456 case '-':
457 case 'e':
458 case 'E':
459 while (1) {
460 int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
461 if (l >= 0 && new) break;
462 pause_nomem();
463 }
464 free(ld->inst);
465 ld->inst = new;
466 break;
467 case 's': {
468 static const struct suffix_mult km_suffixes[] = {
469 { "k", 1024 },
470 { "m", 1024*1024 },
471 { NULL, 0 }
472 };
473 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
474 break;
475 }
476 case 'n':
477 ld->nmax = xatoi_u(&s[1]);
478 break;
479 case 'N':
480 ld->nmin = xatoi_u(&s[1]);
481 break;
482 case 't': {
483 static const struct suffix_mult mh_suffixes[] = {
484 { "m", 60 },
485 { "h", 60*60 },
486 /*{ "d", 24*60*60 },*/
487 { NULL, 0 }
488 };
489 ld->tmax = xatou_sfx(&s[1], mh_suffixes);
490 if (ld->tmax) {
491 taia_uint(&ld->trotate, ld->tmax);
492 taia_add(&ld->trotate, &now, &ld->trotate);
493 if (!tmaxflag || taia_less(&ld->trotate, &trotate))
494 trotate = ld->trotate;
495 tmaxflag = 1;
496 }
497 break;
498 }
499 case '!':
500 if (s[1]) {
501 free(ld->processor);
502 ld->processor = wstrdup(s);
503 }
504 break;
505 }
506 s = np;
507 }
508 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
509 s = ld->inst;
510 while (s) {
511 np = strchr(s, '\n');
512 if (np) *np++ = '\0';
513 s = np;
514 }
515 }
516
517 /* open current */
518 i = stat("current", &st);
519 if (i != -1) {
520 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
521 ld->fnsave[25] = '.';
522 ld->fnsave[26] = 'u';
523 ld->fnsave[27] = '\0';
524 do {
525 taia_now(&now);
526 fmt_taia(ld->fnsave, &now);
527 errno = 0;
528 } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
529 while (rename("current", ld->fnsave) == -1)
530 pause2cannot("rename current", ld->name);
531 rmoldest(ld);
532 i = -1;
533 } else {
534 /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
535 /* (bug in original svlogd. remove this comment when fixed there) */
536 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
537 }
538 } else {
539 if (errno != ENOENT) {
540 logdir_close(ld);
541 warn2("cannot stat current", ld->name);
542 while (fchdir(fdwdir) == -1)
543 pause1cannot("change to initial working directory");
544 return 0;
545 }
546 }
547 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
548 pause2cannot("open current", ld->name);
549 coe(ld->fdcur);
550 while (fchmod(ld->fdcur, 0644) == -1)
551 pause2cannot("set mode of current", ld->name);
552
553 if (verbose) {
554 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
555 else bb_error_msg(INFO"new: %s/current", ld->name);
556 }
557
558 while (fchdir(fdwdir) == -1)
559 pause1cannot("change to initial working directory");
560 return 1;
561}
562
563static void logdirs_reopen(void)
564{
565 struct taia now;
566 int l;
567 int ok = 0;
568
569 tmaxflag = 0;
570 taia_now(&now);
571 for (l = 0; l < dirn; ++l) {
572 logdir_close(&dir[l]);
573 if (logdir_open(&dir[l], fndir[l])) ok = 1;
574 }
575 if (!ok) fatalx("no functional log directories");
576}
577
578/* Used for reading stdin */
579static int buffer_pread(int fd, char *s, unsigned len)
580{
581 struct taia now;
582 int i;
583
584 if (rotateasap) {
585 for (i = 0; i < dirn; ++i)
586 rotate(dir+i);
587 rotateasap = 0;
588 }
589 if (exitasap) {
590 if (linecomplete)
591 return 0;
592 len = 1;
593 }
594 if (reopenasap) {
595 logdirs_reopen();
596 reopenasap = 0;
597 }
598 taia_now(&now);
599 taia_uint(&trotate, 2744);
600 taia_add(&trotate, &now, &trotate);
601 for (i = 0; i < dirn; ++i)
602 if (dir[i].tmax) {
603 if (taia_less(&dir[i].trotate, &now))
604 rotate(dir+i);
605 if (taia_less(&dir[i].trotate, &trotate))
606 trotate = dir[i].trotate;
607 }
608
609 while (1) {
610 /* Comment? */
611 sig_unblock(sig_term);
612 sig_unblock(sig_child);
613 sig_unblock(sig_alarm);
614 sig_unblock(sig_hangup);
615 iopause(&in, 1, &trotate, &now);
616 sig_block(sig_term);
617 sig_block(sig_child);
618 sig_block(sig_alarm);
619 sig_block(sig_hangup);
620 i = safe_read(fd, s, len);
621 if (i >= 0) break;
622 if (errno != EAGAIN) {
623 warn("cannot read standard input");
624 break;
625 }
626 /* else: EAGAIN - normal, repeat silently */
627 }
628
629 if (i > 0) {
630 int cnt;
631 linecomplete = (s[i-1] == '\n');
632 if (!repl) return i;
633
634 cnt = i;
635 while (--cnt >= 0) {
636 char ch = *s;
637 if (ch != '\n') {
638 if (ch < 32 || ch > 126)
639 *s = repl;
640 else {
641 int j;
642 for (j = 0; replace[j]; ++j) {
643 if (ch == replace[j]) {
644 *s = repl;
645 break;
646 }
647 }
648 }
649 }
650 s++;
651 }
652 }
653 return i;
654}
655
656
657static void sig_term_handler(int sig_no)
658{
659 if (verbose)
660 bb_error_msg(INFO"sig%s received", "term");
661 exitasap = 1;
662}
663
664static void sig_child_handler(int sig_no)
665{
666 int pid, l;
667
668 if (verbose)
669 bb_error_msg(INFO"sig%s received", "child");
670 while ((pid = wait_nohang(&wstat)) > 0)
671 for (l = 0; l < dirn; ++l)
672 if (dir[l].ppid == pid) {
673 dir[l].ppid = 0;
674 processorstop(&dir[l]);
675 break;
676 }
677}
678
679static void sig_alarm_handler(int sig_no)
680{
681 if (verbose)
682 bb_error_msg(INFO"sig%s received", "alarm");
683 rotateasap = 1;
684}
685
686static void sig_hangup_handler(int sig_no)
687{
688 if (verbose)
689 bb_error_msg(INFO"sig%s received", "hangup");
690 reopenasap = 1;
691}
692
693static void logmatch(struct logdir *ld)
694{
695 char *s;
696
697 ld->match = '+';
698 ld->matcherr = 'E';
699 s = ld->inst;
700 while (s && s[0]) {
701 switch (s[0]) {
702 case '+':
703 case '-':
704 if (pmatch(s+1, line, linelen))
705 ld->match = s[0];
706 break;
707 case 'e':
708 case 'E':
709 if (pmatch(s+1, line, linelen))
710 ld->matcherr = s[0];
711 break;
712 }
713 s += strlen(s) + 1;
714 }
715}
716
717int svlogd_main(int argc, char **argv)
718{
719 struct taia now;
720 char *r,*l,*b;
721 ssize_t stdin_cnt = 0;
722 int i;
723 unsigned opt;
724 unsigned timestamp = 0;
725
726 opt_complementary = "tt:vv";
727 opt = getopt32(argc, argv, "r:R:l:b:tv",
728 &r, &replace, &l, &b, &timestamp, &verbose);
729 if (opt & 1) { // -r
730 repl = r[0];
731 if (!repl || r[1]) usage();
732 }
733 if (opt & 2) if (!repl) repl = '_'; // -R
734 if (opt & 4) { // -l
735 linemax = xatou_range(l, 0, 1000);
736 if (linemax == 0) linemax = 1000;
737 if (linemax < 256) linemax = 256;
738 }
739 if (opt & 8) { // -b
740 buflen = xatoi_u(b);
741 if (buflen == 0) buflen = 1024;
742 }
743 //if (opt & 0x10) timestamp++; // -t
744 //if (opt & 0x20) verbose++; // -v
745 if (timestamp > 2) timestamp = 2;
746 argv += optind;
747 argc -= optind;
748
749 dirn = argc;
750 if (dirn <= 0) usage();
751 if (buflen <= linemax) usage();
752 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
753 coe(fdwdir);
754 dir = xmalloc(dirn * sizeof(struct logdir));
755 for (i = 0; i < dirn; ++i) {
756 dir[i].fddir = -1;
757 dir[i].fdcur = -1;
758 dir[i].btmp = xmalloc(buflen);
759 dir[i].ppid = 0;
760 }
761 line = xmalloc(linemax + (timestamp ? 26 : 0));
762 fndir = argv;
763 in.fd = 0;
764 in.events = IOPAUSE_READ;
765 ndelay_on(in.fd);
766
767 sig_block(sig_term);
768 sig_block(sig_child);
769 sig_block(sig_alarm);
770 sig_block(sig_hangup);
771 sig_catch(sig_term, sig_term_handler);
772 sig_catch(sig_child, sig_child_handler);
773 sig_catch(sig_alarm, sig_alarm_handler);
774 sig_catch(sig_hangup, sig_hangup_handler);
775
776 logdirs_reopen();
777
778 /* Each iteration processes one line */
779 while (1) {
780 int printlen;
781 char *lineptr = line;
782 char *np;
783 char ch;
784
785 /* Prepare timestamp if needed */
786 if (timestamp) {
787 char stamp[FMT_PTIME];
788 taia_now(&now);
789 switch (timestamp) {
790 case 1:
791 fmt_taia(stamp, &now);
792 break;
793 default: /* case 2: */
794 fmt_ptime(stamp, &now);
795 break;
796 }
797 memcpy(line, stamp, 25);
798 line[25] = ' ';
799 lineptr += 26;
800 }
801
802 /* lineptr[0..linemax-1] - buffer for stdin */
803 /* (possibly has some unprocessed data from prev loop) */
804
805 /* Refill the buffer if needed */
806 np = memchr(lineptr, '\n', stdin_cnt);
807 i = linemax - stdin_cnt; /* avail. bytes at tail */
808 if (i >= 128 && !exitasap && !np) {
809 int sz = buffer_pread(0, lineptr + stdin_cnt, i);
810 if (sz <= 0) /* EOF or error on stdin */
811 exitasap = 1;
812 else {
813 stdin_cnt += sz;
814 np = memchr(lineptr, '\n', stdin_cnt);
815 }
816 }
817 if (stdin_cnt <= 0 && exitasap)
818 break;
819
820 /* Search for '\n' (in fact, np already holds the result) */
821 linelen = stdin_cnt;
822 if (np) linelen = np - lineptr + 1;
823 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
824 ch = lineptr[linelen-1];
825
826 printlen = linelen + (timestamp ? 26 : 0);
827 /* write out line[0..printlen-1] to each log destination */
828 for (i = 0; i < dirn; ++i) {
829 struct logdir *ld = &dir[i];
830 if (ld->fddir == -1) continue;
831 if (ld->inst)
832 logmatch(ld);
833 if (ld->matcherr == 'e')
834 full_write(2, line, printlen);
835 if (ld->match != '+') continue;
836 buffer_pwrite(i, line, printlen);
837 }
838
839 /* If we didn't see '\n' (long input line), */
840 /* read/write repeatedly until we see it */
841 while (ch != '\n') {
842 /* lineptr is emptied now, safe to use as buffer */
843 stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
844 if (stdin_cnt <= 0) { /* EOF or error on stdin */
845 lineptr[0] = ch = '\n';
846 linelen = 1;
847 exitasap = 1;
848 stdin_cnt = 1;
849 } else {
850 linelen = stdin_cnt;
851 np = memchr(lineptr, '\n', stdin_cnt);
852 if (np) linelen = np - lineptr + 1;
853 ch = lineptr[linelen-1];
854 }
855 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
856 for (i = 0; i < dirn; ++i) {
857 if (dir[i].fddir == -1) continue;
858 if (dir[i].matcherr == 'e')
859 full_write(2, lineptr, linelen);
860 if (dir[i].match != '+') continue;
861 buffer_pwrite(i, lineptr, linelen);
862 }
863 }
864
865 /* Move unprocessed data to the front of line */
866 stdin_cnt -= linelen;
867 if (stdin_cnt > 0) /* TODO: slow if buffer is big */
868 memmove(lineptr, &lineptr[linelen], stdin_cnt);
869 }
870
871 for (i = 0; i < dirn; ++i) {
872 if (dir[i].ppid)
873 while (!processorstop(&dir[i]))
874 /* repeat */;
875 logdir_close(&dir[i]);
876 }
877 _exit(0);
878}