diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-27 04:35:04 +0000 |
commit | 8d42f86b146871ae4c4cafd3801a85f381249a14 (patch) | |
tree | b963999fc54eddb65f1929b894f868e24851fc9c /runit | |
download | busybox-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.in | 66 | ||||
-rw-r--r-- | runit/Kbuild | 12 | ||||
-rw-r--r-- | runit/chpst.c | 373 | ||||
-rw-r--r-- | runit/runit_lib.c | 982 | ||||
-rw-r--r-- | runit/runit_lib.h | 403 | ||||
-rw-r--r-- | runit/runsv.c | 613 | ||||
-rw-r--r-- | runit/runsvdir.c | 306 | ||||
-rw-r--r-- | runit/sv.c | 360 | ||||
-rw-r--r-- | runit/svlogd.c | 878 |
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 | |||
6 | menu "Runit Utilities" | ||
7 | |||
8 | config RUNSV | ||
9 | bool "runsv" | ||
10 | default n | ||
11 | help | ||
12 | runsv starts and monitors a service and optionally an appendant log | ||
13 | service. | ||
14 | |||
15 | config 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 | |||
23 | config 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 | |||
30 | config 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 | |||
38 | config 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 | |||
45 | config SETUIDGID | ||
46 | bool "setuidgid" | ||
47 | help | ||
48 | Sets soft resource limits as specified by options | ||
49 | |||
50 | config ENVUIDGID | ||
51 | bool "envuidgid" | ||
52 | help | ||
53 | Sets $UID to account's uid and $GID to account's gid | ||
54 | |||
55 | config ENVDIR | ||
56 | bool "envdir" | ||
57 | help | ||
58 | Sets various environment variables as specified by files | ||
59 | in the given directory | ||
60 | |||
61 | config SOFTLIMIT | ||
62 | bool "softlimit" | ||
63 | help | ||
64 | Sets soft resource limits as specified by options | ||
65 | |||
66 | endmenu | ||
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 | |||
7 | lib-y:= | ||
8 | lib-$(CONFIG_RUNSV) += runsv.o runit_lib.o | ||
9 | lib-$(CONFIG_RUNSVDIR) += runsvdir.o runit_lib.o | ||
10 | lib-$(CONFIG_SV) += sv.o runit_lib.o | ||
11 | lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o | ||
12 | lib-$(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 | /* | ||
2 | Copyright (c) 2001-2006, Gerrit Pape | ||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | 1. Redistributions of source code must retain the above copyright notice, | ||
9 | this list of conditions and the following disclaimer. | ||
10 | 2. Redistributions in binary form must reproduce the above copyright | ||
11 | notice, this list of conditions and the following disclaimer in the | ||
12 | documentation and/or other materials provided with the distribution. | ||
13 | 3. The name of the author may not be used to endorse or promote products | ||
14 | derived from this software without specific prior written permission. | ||
15 | |||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
19 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
22 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | /* 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 | |||
42 | static char *set_user; | ||
43 | static char *env_user; | ||
44 | static const char *env_dir; | ||
45 | static long limitd = -2; | ||
46 | static long limits = -2; | ||
47 | static long limitl = -2; | ||
48 | static long limita = -2; | ||
49 | static long limito = -2; | ||
50 | static long limitp = -2; | ||
51 | static long limitf = -2; | ||
52 | static long limitc = -2; | ||
53 | static long limitr = -2; | ||
54 | static long limitt = -2; | ||
55 | static int nicelvl; | ||
56 | static const char *root; | ||
57 | |||
58 | static 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 | |||
71 | static 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 | |||
82 | static 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 | |||
146 | static 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 | |||
158 | static 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] */ | ||
253 | static void setuidgid(int, char **); | ||
254 | static void envuidgid(int, char **); | ||
255 | static void envdir(int, char **); | ||
256 | static void softlimit(int, char **); | ||
257 | |||
258 | int 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 | |||
316 | static 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 | |||
328 | static 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 | |||
340 | static 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 | |||
352 | static 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 | /* | ||
2 | Copyright (c) 2001-2006, Gerrit Pape | ||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | 1. Redistributions of source code must retain the above copyright notice, | ||
9 | this list of conditions and the following disclaimer. | ||
10 | 2. Redistributions in binary form must reproduce the above copyright | ||
11 | notice, this list of conditions and the following disclaimer in the | ||
12 | documentation and/or other materials provided with the distribution. | ||
13 | 3. The name of the author may not be used to endorse or promote products | ||
14 | derived from this software without specific prior written permission. | ||
15 | |||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
19 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
22 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | /* 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 | |||
39 | void 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 | |||
51 | static 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 | |||
62 | static 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 | |||
71 | int 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 | |||
84 | int 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 | |||
94 | int 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 | |||
104 | char *buffer_peek(buffer *s) | ||
105 | { | ||
106 | return s->x + s->n; | ||
107 | } | ||
108 | |||
109 | void buffer_seek(buffer *s,unsigned len) | ||
110 | { | ||
111 | s->n += len; | ||
112 | s->p -= len; | ||
113 | } | ||
114 | |||
115 | |||
116 | /*** buffer_put.c ***/ | ||
117 | |||
118 | static 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 | |||
135 | int 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 | |||
145 | int 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 | |||
162 | int 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 | |||
184 | int 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 | |||
190 | int buffer_putsalign(buffer *s,const char *buf) | ||
191 | { | ||
192 | return buffer_putalign(s,buf,strlen(buf)); | ||
193 | } | ||
194 | |||
195 | int buffer_puts(buffer *s,const char *buf) | ||
196 | { | ||
197 | return buffer_put(s,buf,strlen(buf)); | ||
198 | } | ||
199 | |||
200 | int buffer_putsflush(buffer *s,const char *buf) | ||
201 | { | ||
202 | return buffer_putflush(s,buf,strlen(buf)); | ||
203 | } | ||
204 | |||
205 | |||
206 | /*** buffer_read.c ***/ | ||
207 | |||
208 | int buffer_unixread(int fd,char *buf,unsigned len) | ||
209 | { | ||
210 | return read(fd,buf,len); | ||
211 | } | ||
212 | |||
213 | |||
214 | /*** buffer_write.c ***/ | ||
215 | |||
216 | int buffer_unixwrite(int fd,char *buf,unsigned len) | ||
217 | { | ||
218 | return write(fd,buf,len); | ||
219 | } | ||
220 | |||
221 | |||
222 | /*** byte_chr.c ***/ | ||
223 | |||
224 | unsigned 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 | |||
243 | int coe(int fd) | ||
244 | { | ||
245 | return fcntl(fd,F_SETFD,FD_CLOEXEC); | ||
246 | } | ||
247 | |||
248 | |||
249 | /*** fd_copy.c ***/ | ||
250 | |||
251 | int 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 | |||
263 | int 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 | |||
274 | int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); } | ||
275 | |||
276 | |||
277 | /*** fmt_ptime.c ***/ | ||
278 | |||
279 | unsigned 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 | |||
296 | unsigned 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 | |||
313 | unsigned fmt_uint(char *s,unsigned u) | ||
314 | { | ||
315 | return fmt_ulong(s,u); | ||
316 | } | ||
317 | |||
318 | |||
319 | /*** fmt_uint0.c ***/ | ||
320 | |||
321 | unsigned 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 | |||
333 | unsigned 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 | |||
348 | void tai_now(struct tai *t) | ||
349 | { | ||
350 | tai_unix(t,time((time_t *) 0)); | ||
351 | } | ||
352 | |||
353 | |||
354 | /*** tai_pack.c ***/ | ||
355 | |||
356 | void 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 | |||
374 | void 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 | |||
382 | void 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 | |||
402 | void 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 | |||
420 | double taia_approx(const struct taia *t) | ||
421 | { | ||
422 | return tai_approx(&t->sec) + taia_frac(t); | ||
423 | } | ||
424 | |||
425 | |||
426 | /*** taia_frac.c ***/ | ||
427 | |||
428 | double 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 | |||
438 | int 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 | |||
450 | void 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 | |||
462 | void 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 | |||
486 | void 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 | |||
509 | void 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 | |||
520 | int 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 | |||
528 | int 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 | |||
541 | int stralloc_cats(stralloc *sa,const char *s) | ||
542 | { | ||
543 | return stralloc_catb(sa,s,strlen(s)); | ||
544 | } | ||
545 | |||
546 | |||
547 | /*** stralloc_eady.c ***/ | ||
548 | |||
549 | GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready) | ||
550 | GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus) | ||
551 | |||
552 | |||
553 | /*** stralloc_opyb.c ***/ | ||
554 | |||
555 | int 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 | |||
567 | int stralloc_copys(stralloc *sa,const char *s) | ||
568 | { | ||
569 | return stralloc_copyb(sa,s,strlen(s)); | ||
570 | } | ||
571 | |||
572 | |||
573 | /*** stralloc_pend.c ***/ | ||
574 | |||
575 | GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append) | ||
576 | |||
577 | #endif /* stralloc */ | ||
578 | |||
579 | /*** iopause.c ***/ | ||
580 | |||
581 | void 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 | |||
610 | int lock_ex(int fd) | ||
611 | { | ||
612 | return flock(fd,LOCK_EX); | ||
613 | } | ||
614 | |||
615 | |||
616 | /*** lock_exnb.c ***/ | ||
617 | |||
618 | int lock_exnb(int fd) | ||
619 | { | ||
620 | return flock(fd,LOCK_EX | LOCK_NB); | ||
621 | } | ||
622 | |||
623 | |||
624 | /*** open_append.c ***/ | ||
625 | |||
626 | int 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 | |||
634 | int open_read(const char *fn) | ||
635 | { | ||
636 | return open(fn, O_RDONLY|O_NDELAY); | ||
637 | } | ||
638 | |||
639 | |||
640 | /*** open_trunc.c ***/ | ||
641 | |||
642 | int 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 | |||
650 | int open_write(const char *fn) | ||
651 | { | ||
652 | return open(fn, O_WRONLY|O_NDELAY); | ||
653 | } | ||
654 | |||
655 | |||
656 | /*** openreadclose.c ***/ | ||
657 | #if 0 | ||
658 | int 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 | ||
674 | static stralloc plus; | ||
675 | static stralloc tmp; | ||
676 | |||
677 | int 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 | |||
689 | void 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 | ||
738 | static stralloc tmp; | ||
739 | |||
740 | void 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 | |||
782 | unsigned 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 | |||
825 | int 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 | |||
832 | int prot_uid(int uid) | ||
833 | { | ||
834 | return setuid(uid); | ||
835 | } | ||
836 | |||
837 | |||
838 | /*** readclose.c ***/ | ||
839 | #if 0 | ||
840 | int 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 | |||
852 | int 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 | |||
861 | unsigned 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 | |||
877 | int 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 | |||
885 | int sig_alarm = SIGALRM; | ||
886 | int sig_child = SIGCHLD; | ||
887 | int sig_cont = SIGCONT; | ||
888 | int sig_hangup = SIGHUP; | ||
889 | int sig_int = SIGINT; | ||
890 | int sig_pipe = SIGPIPE; | ||
891 | int sig_term = SIGTERM; | ||
892 | |||
893 | void (*sig_defaulthandler)(int) = SIG_DFL; | ||
894 | void (*sig_ignorehandler)(int) = SIG_IGN; | ||
895 | |||
896 | |||
897 | /*** sig_block.c ***/ | ||
898 | |||
899 | void 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 | |||
907 | void 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 | |||
915 | void 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 | |||
925 | void 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 | |||
937 | void sig_pause(void) | ||
938 | { | ||
939 | sigset_t ss; | ||
940 | sigemptyset(&ss); | ||
941 | sigsuspend(&ss); | ||
942 | } | ||
943 | |||
944 | |||
945 | /*** str_chr.c ***/ | ||
946 | |||
947 | unsigned 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 | |||
966 | int wait_nohang(int *wstat) | ||
967 | { | ||
968 | return waitpid(-1,wstat,WNOHANG); | ||
969 | } | ||
970 | |||
971 | |||
972 | /*** wait_pid.c ***/ | ||
973 | |||
974 | int 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 | /* | ||
2 | Copyright (c) 2001-2006, Gerrit Pape | ||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | 1. Redistributions of source code must retain the above copyright notice, | ||
9 | this list of conditions and the following disclaimer. | ||
10 | 2. Redistributions in binary form must reproduce the above copyright | ||
11 | notice, this list of conditions and the following disclaimer in the | ||
12 | documentation and/or other materials provided with the distribution. | ||
13 | 3. The name of the author may not be used to endorse or promote products | ||
14 | derived from this software without specific prior written permission. | ||
15 | |||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
19 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
22 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | /*** buffer.h ***/ | ||
29 | |||
30 | typedef 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 | |||
42 | extern void buffer_init(buffer *,int (*)(int fd,char *buf,unsigned len),int,char *,unsigned); | ||
43 | |||
44 | extern int buffer_flush(buffer *); | ||
45 | extern int buffer_put(buffer *,const char *,unsigned); | ||
46 | extern int buffer_putalign(buffer *,const char *,unsigned); | ||
47 | extern int buffer_putflush(buffer *,const char *,unsigned); | ||
48 | extern int buffer_puts(buffer *,const char *); | ||
49 | extern int buffer_putsalign(buffer *,const char *); | ||
50 | extern 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 | |||
58 | extern int buffer_get(buffer *,char *,unsigned); | ||
59 | extern int buffer_bget(buffer *,char *,unsigned); | ||
60 | extern int buffer_feed(buffer *); | ||
61 | |||
62 | extern char *buffer_peek(buffer *); | ||
63 | extern 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 | |||
74 | extern int buffer_copy(buffer *,buffer *); | ||
75 | |||
76 | extern int buffer_unixread(int,char *,unsigned); | ||
77 | /* Actually, int buffer_unixwrite(int,const char *,unsigned), | ||
78 | but that 'const' will produce warnings... oh well */ | ||
79 | extern int buffer_unixwrite(int,char *,unsigned); | ||
80 | |||
81 | |||
82 | /*** byte.h ***/ | ||
83 | |||
84 | extern unsigned byte_chr(char *s,unsigned n,int c); | ||
85 | |||
86 | |||
87 | /*** coe.h ***/ | ||
88 | |||
89 | extern int coe(int); | ||
90 | |||
91 | |||
92 | /*** direntry.h ***/ | ||
93 | |||
94 | #define direntry struct dirent | ||
95 | |||
96 | |||
97 | /*** fd.h ***/ | ||
98 | |||
99 | extern int fd_copy(int,int); | ||
100 | extern int fd_move(int,int); | ||
101 | |||
102 | |||
103 | /*** fifo.h ***/ | ||
104 | |||
105 | extern 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 | |||
113 | extern unsigned fmt_uint(char *,unsigned); | ||
114 | extern unsigned fmt_uint0(char *,unsigned,unsigned); | ||
115 | extern unsigned fmt_xint(char *,unsigned); | ||
116 | extern unsigned fmt_nbbint(char *,unsigned,unsigned,unsigned,unsigned); | ||
117 | extern unsigned fmt_ushort(char *,unsigned short); | ||
118 | extern unsigned fmt_xshort(char *,unsigned short); | ||
119 | extern unsigned fmt_nbbshort(char *,unsigned,unsigned,unsigned,unsigned short); | ||
120 | extern unsigned fmt_ulong(char *,unsigned long); | ||
121 | extern unsigned fmt_xlong(char *,unsigned long); | ||
122 | extern unsigned fmt_nbblong(char *,unsigned,unsigned,unsigned,unsigned long); | ||
123 | |||
124 | extern unsigned fmt_plusminus(char *,int); | ||
125 | extern unsigned fmt_minus(char *,int); | ||
126 | extern unsigned fmt_0x(char *,int); | ||
127 | |||
128 | extern unsigned fmt_str(char *,const char *); | ||
129 | extern unsigned fmt_strn(char *,const char *,unsigned); | ||
130 | |||
131 | |||
132 | /*** tai.h ***/ | ||
133 | |||
134 | struct tai { | ||
135 | uint64_t x; | ||
136 | } ; | ||
137 | |||
138 | #define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u))) | ||
139 | |||
140 | extern void tai_now(struct tai *); | ||
141 | |||
142 | #define tai_approx(t) ((double) ((t)->x)) | ||
143 | |||
144 | extern void tai_add(struct tai *,const struct tai *,const struct tai *); | ||
145 | extern 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 | ||
149 | extern void tai_pack(char *,const struct tai *); | ||
150 | extern void tai_unpack(const char *,struct tai *); | ||
151 | |||
152 | extern void tai_uint(struct tai *,unsigned); | ||
153 | |||
154 | |||
155 | /*** taia.h ***/ | ||
156 | |||
157 | struct taia { | ||
158 | struct tai sec; | ||
159 | unsigned long nano; /* 0...999999999 */ | ||
160 | unsigned long atto; /* 0...999999999 */ | ||
161 | } ; | ||
162 | |||
163 | extern void taia_tai(const struct taia *,struct tai *); | ||
164 | |||
165 | extern void taia_now(struct taia *); | ||
166 | |||
167 | extern double taia_approx(const struct taia *); | ||
168 | extern double taia_frac(const struct taia *); | ||
169 | |||
170 | extern void taia_add(struct taia *,const struct taia *,const struct taia *); | ||
171 | extern void taia_addsec(struct taia *,const struct taia *,int); | ||
172 | extern void taia_sub(struct taia *,const struct taia *,const struct taia *); | ||
173 | extern void taia_half(struct taia *,const struct taia *); | ||
174 | extern int taia_less(const struct taia *,const struct taia *); | ||
175 | |||
176 | #define TAIA_PACK 16 | ||
177 | extern void taia_pack(char *,const struct taia *); | ||
178 | extern void taia_unpack(const char *,struct taia *); | ||
179 | |||
180 | #define TAIA_FMTFRAC 19 | ||
181 | extern unsigned taia_fmtfrac(char *,const struct taia *); | ||
182 | |||
183 | extern void taia_uint(struct taia *,unsigned); | ||
184 | |||
185 | |||
186 | /*** fmt_ptime.h ***/ | ||
187 | |||
188 | #define FMT_PTIME 30 | ||
189 | |||
190 | extern unsigned fmt_ptime(char *, struct taia *); | ||
191 | extern 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) \ | ||
203 | int 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) \ | ||
217 | int 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) \ | ||
231 | int 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 | ||
237 | GEN_ALLOC_typedef(stralloc,char,s,len,a) | ||
238 | |||
239 | extern int stralloc_ready(stralloc *,unsigned); | ||
240 | extern int stralloc_readyplus(stralloc *,unsigned); | ||
241 | extern int stralloc_copy(stralloc *,const stralloc *); | ||
242 | extern int stralloc_cat(stralloc *,const stralloc *); | ||
243 | extern int stralloc_copys(stralloc *,const char *); | ||
244 | extern int stralloc_cats(stralloc *,const char *); | ||
245 | extern int stralloc_copyb(stralloc *,const char *,unsigned); | ||
246 | extern int stralloc_catb(stralloc *,const char *,unsigned); | ||
247 | extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */ | ||
248 | extern int stralloc_starts(stralloc *,const char *); | ||
249 | |||
250 | #define stralloc_0(sa) stralloc_append(sa,"") | ||
251 | |||
252 | extern int stralloc_catulong0(stralloc *,unsigned long,unsigned); | ||
253 | extern 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 | |||
263 | typedef struct pollfd iopause_fd; | ||
264 | #define IOPAUSE_READ POLLIN | ||
265 | #define IOPAUSE_WRITE POLLOUT | ||
266 | |||
267 | extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *); | ||
268 | |||
269 | |||
270 | /*** lock.h ***/ | ||
271 | |||
272 | extern int lock_ex(int); | ||
273 | extern int lock_un(int); | ||
274 | extern int lock_exnb(int); | ||
275 | |||
276 | |||
277 | /*** ndelay.h ***/ | ||
278 | |||
279 | extern int ndelay_on(int); | ||
280 | extern int ndelay_off(int); | ||
281 | |||
282 | |||
283 | /*** open.h ***/ | ||
284 | |||
285 | extern int open_read(const char *); | ||
286 | extern int open_excl(const char *); | ||
287 | extern int open_append(const char *); | ||
288 | extern int open_trunc(const char *); | ||
289 | extern int open_write(const char *); | ||
290 | |||
291 | |||
292 | /*** openreadclose.h ***/ | ||
293 | #if 0 | ||
294 | extern int openreadclose(const char *,stralloc *,unsigned); | ||
295 | #endif | ||
296 | |||
297 | /*** pathexec.h ***/ | ||
298 | |||
299 | extern void pathexec_run(const char *,char *const *,char *const *); | ||
300 | extern int pathexec_env(const char *,const char *); | ||
301 | extern void pathexec(char **); | ||
302 | |||
303 | |||
304 | /*** pmatch.h ***/ | ||
305 | |||
306 | extern unsigned pmatch(const char *, const char *, unsigned); | ||
307 | |||
308 | |||
309 | /*** prot.h ***/ | ||
310 | |||
311 | extern int prot_gid(int); | ||
312 | extern int prot_uid(int); | ||
313 | |||
314 | |||
315 | /*** readclose.h ***/ | ||
316 | #if 0 | ||
317 | extern int readclose_append(int,stralloc *,unsigned); | ||
318 | extern int readclose(int,stralloc *,unsigned); | ||
319 | #endif | ||
320 | |||
321 | /*** scan.h ***/ | ||
322 | |||
323 | extern unsigned scan_uint(const char *,unsigned *); | ||
324 | extern unsigned scan_xint(const char *,unsigned *); | ||
325 | extern unsigned scan_nbbint(const char *,unsigned,unsigned,unsigned,unsigned *); | ||
326 | extern unsigned scan_ushort(const char *,unsigned short *); | ||
327 | extern unsigned scan_xshort(const char *,unsigned short *); | ||
328 | extern unsigned scan_nbbshort(const char *,unsigned,unsigned,unsigned,unsigned short *); | ||
329 | extern unsigned scan_ulong(const char *,unsigned long *); | ||
330 | extern unsigned scan_xlong(const char *,unsigned long *); | ||
331 | extern unsigned scan_nbblong(const char *,unsigned,unsigned,unsigned,unsigned long *); | ||
332 | |||
333 | extern unsigned scan_plusminus(const char *,int *); | ||
334 | extern unsigned scan_0x(const char *,unsigned *); | ||
335 | |||
336 | extern unsigned scan_whitenskip(const char *,unsigned); | ||
337 | extern unsigned scan_nonwhitenskip(const char *,unsigned); | ||
338 | extern unsigned scan_charsetnskip(const char *,const char *,unsigned); | ||
339 | extern unsigned scan_noncharsetnskip(const char *,const char *,unsigned); | ||
340 | |||
341 | extern unsigned scan_strncmp(const char *,const char *,unsigned); | ||
342 | extern unsigned scan_memcmp(const char *,const char *,unsigned); | ||
343 | |||
344 | extern unsigned scan_long(const char *,long *); | ||
345 | extern unsigned scan_8long(const char *,unsigned long *); | ||
346 | |||
347 | |||
348 | /*** seek.h ***/ | ||
349 | |||
350 | typedef unsigned long seek_pos; | ||
351 | |||
352 | extern seek_pos seek_cur(int); | ||
353 | |||
354 | extern int seek_set(int,seek_pos); | ||
355 | extern int seek_end(int); | ||
356 | |||
357 | extern int seek_trunc(int,seek_pos); | ||
358 | |||
359 | #define seek_begin(fd) (seek_set((fd),(seek_pos) 0)) | ||
360 | |||
361 | |||
362 | /*** sig.h ***/ | ||
363 | |||
364 | extern int sig_alarm; | ||
365 | extern int sig_child; | ||
366 | extern int sig_cont; | ||
367 | extern int sig_hangup; | ||
368 | extern int sig_int; | ||
369 | extern int sig_pipe; | ||
370 | extern int sig_term; | ||
371 | |||
372 | extern void (*sig_defaulthandler)(int); | ||
373 | extern void (*sig_ignorehandler)(int); | ||
374 | |||
375 | extern 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 | |||
379 | extern void sig_block(int); | ||
380 | extern void sig_unblock(int); | ||
381 | extern void sig_blocknone(void); | ||
382 | extern void sig_pause(void); | ||
383 | |||
384 | extern void sig_dfl(int); | ||
385 | |||
386 | |||
387 | /*** str.h ***/ | ||
388 | |||
389 | extern 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 | |||
397 | extern int wait_pid(int *wstat, int pid); | ||
398 | extern 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 | |||
9 | static 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 | |||
24 | struct 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 | }; | ||
35 | static struct svdir svd[2]; | ||
36 | |||
37 | static int sigterm = 0; | ||
38 | static int haslog = 0; | ||
39 | static int pidchanged = 1; | ||
40 | static int logpipe[2]; | ||
41 | static char *dir; | ||
42 | |||
43 | #define usage() bb_show_usage() | ||
44 | |||
45 | static 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 | } | ||
50 | static void fatal_cannot(char *m) | ||
51 | { | ||
52 | fatal2_cannot(m, ""); | ||
53 | /* was exiting 111 */ | ||
54 | } | ||
55 | static 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 | } | ||
60 | static void warn_cannot(char *m) | ||
61 | { | ||
62 | bb_perror_msg("%s: warning: cannot %s", dir, m); | ||
63 | } | ||
64 | static void warnx_cannot(char *m) | ||
65 | { | ||
66 | bb_error_msg("%s: warning: cannot %s", dir, m); | ||
67 | } | ||
68 | |||
69 | static void stopservice(struct svdir *); | ||
70 | |||
71 | static void s_child(int sig_no) | ||
72 | { | ||
73 | write(selfpipe[1], "", 1); | ||
74 | } | ||
75 | |||
76 | static void s_term(int sig_no) | ||
77 | { | ||
78 | sigterm = 1; | ||
79 | write(selfpipe[1], "", 1); /* XXX */ | ||
80 | } | ||
81 | |||
82 | static 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 | |||
91 | static 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 | |||
100 | static 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 | |||
110 | static 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 | |||
219 | static 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 | |||
260 | static 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 | |||
277 | static 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 | |||
330 | static 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 | |||
393 | int 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 | |||
11 | static char *svdir; | ||
12 | static unsigned long dev; | ||
13 | static unsigned long ino; | ||
14 | static struct service { | ||
15 | unsigned long dev; | ||
16 | unsigned long ino; | ||
17 | int pid; | ||
18 | int isgone; | ||
19 | } *sv; | ||
20 | static int svnum; | ||
21 | static int check = 1; | ||
22 | static char *rplog; | ||
23 | static int rploglen; | ||
24 | static int logpipe[2]; | ||
25 | static iopause_fd io[1]; | ||
26 | static struct taia stamplog; | ||
27 | static int exitsoon; | ||
28 | static int pgrp; | ||
29 | |||
30 | #define usage() bb_show_usage() | ||
31 | static 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 | } | ||
36 | static void warn3x(char *m1, char *m2, char *m3) | ||
37 | { | ||
38 | bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3); | ||
39 | } | ||
40 | static void warn2_cannot(char *m1, char *m2) | ||
41 | { | ||
42 | warn3x("cannot ", m1, m2); | ||
43 | } | ||
44 | static void warnx(char *m1) | ||
45 | { | ||
46 | warn3x(m1, "", ""); | ||
47 | } | ||
48 | |||
49 | static void s_term(int sig_no) | ||
50 | { | ||
51 | exitsoon = 1; | ||
52 | } | ||
53 | static void s_hangup(int sig_no) | ||
54 | { | ||
55 | exitsoon = 2; | ||
56 | } | ||
57 | |||
58 | static 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 | |||
83 | static 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 | |||
152 | static 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 | |||
177 | int 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 | |||
9 | static char *action; | ||
10 | static char *acts; | ||
11 | static char *varservice = "/var/service/"; | ||
12 | static char **service; | ||
13 | static char **servicex; | ||
14 | static unsigned services; | ||
15 | static unsigned rc = 0; | ||
16 | static unsigned verbose = 0; | ||
17 | static unsigned long waitsec = 7; | ||
18 | static unsigned kll = 0; | ||
19 | static struct taia tstart, tnow, tdiff; | ||
20 | static struct tai tstatus; | ||
21 | |||
22 | static int (*act)(char*) = 0; | ||
23 | static int (*cbk)(char*) = 0; | ||
24 | |||
25 | static int curdir, fd, r; | ||
26 | static char svstatus[20]; | ||
27 | |||
28 | #define usage() bb_show_usage() | ||
29 | |||
30 | static void fatal_cannot(char *m1) | ||
31 | { | ||
32 | bb_perror_msg("fatal: cannot %s", m1); | ||
33 | _exit(151); | ||
34 | } | ||
35 | |||
36 | static 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 | |||
54 | static void fail(char *m1) { ++rc; out(FAIL, m1); } | ||
55 | static void failx(char *m1) { errno = 0; fail(m1); } | ||
56 | static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); } | ||
57 | static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); } | ||
58 | static void ok(char *m1) { errno = 0; out(OK, m1); } | ||
59 | |||
60 | static 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 | |||
86 | static 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 | |||
124 | static 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 | |||
142 | static 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 | |||
174 | static 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 | |||
207 | static 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 | |||
227 | int 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 = ✓ | ||
257 | switch (*action) { | ||
258 | case 'x': case 'e': | ||
259 | acts = "x"; break; | ||
260 | case 'X': case 'E': | ||
261 | acts = "x"; kll = 1; cbk = ✓ break; | ||
262 | case 'D': | ||
263 | acts = "d"; kll = 1; cbk = ✓ break; | ||
264 | case 'T': | ||
265 | acts = "tc"; kll = 1; cbk = ✓ break; | ||
266 | case 'c': | ||
267 | if (!str_diff(action, "check")) { | ||
268 | act = 0; | ||
269 | acts = "c"; | ||
270 | cbk = ✓ | ||
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 = ✓ | ||
280 | break; | ||
281 | } | ||
282 | if (!str_diff(action, "start")) { | ||
283 | acts = "u"; | ||
284 | cbk = ✓ | ||
285 | break; | ||
286 | } | ||
287 | if (!str_diff(action, "stop")) { | ||
288 | acts = "d"; | ||
289 | cbk = ✓ | ||
290 | break; | ||
291 | } | ||
292 | act = &status; cbk = 0; break; | ||
293 | case 'r': | ||
294 | if (!str_diff(action, "restart")) { | ||
295 | acts = "tcu"; | ||
296 | cbk = ✓ | ||
297 | break; | ||
298 | } | ||
299 | usage(); | ||
300 | case 'f': | ||
301 | if (!str_diff(action, "force-reload")) | ||
302 | { acts = "tc"; kll = 1; cbk = ✓ break; } | ||
303 | if (!str_diff(action, "force-restart")) | ||
304 | { acts = "tcu"; kll = 1; cbk = ✓ break; } | ||
305 | if (!str_diff(action, "force-shutdown")) | ||
306 | { acts = "x"; kll = 1; cbk = ✓ break; } | ||
307 | if (!str_diff(action, "force-stop")) | ||
308 | { acts = "d"; kll = 1; cbk = ✓ 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 | /* | ||
2 | Copyright (c) 2001-2006, Gerrit Pape | ||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | 1. Redistributions of source code must retain the above copyright notice, | ||
9 | this list of conditions and the following disclaimer. | ||
10 | 2. Redistributions in binary form must reproduce the above copyright | ||
11 | notice, this list of conditions and the following disclaimer in the | ||
12 | documentation and/or other materials provided with the distribution. | ||
13 | 3. The name of the author may not be used to endorse or promote products | ||
14 | derived from this software without specific prior written permission. | ||
15 | |||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
19 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
22 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | /* 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 | |||
36 | static unsigned verbose; | ||
37 | static int linemax = 1000; | ||
38 | static int buflen = 1024; | ||
39 | static int linelen; | ||
40 | |||
41 | static char **fndir; | ||
42 | static int fdwdir; | ||
43 | static int wstat; | ||
44 | static struct taia trotate; | ||
45 | |||
46 | static char *line; | ||
47 | static unsigned exitasap; | ||
48 | static unsigned rotateasap; | ||
49 | static unsigned reopenasap; | ||
50 | static unsigned linecomplete = 1; | ||
51 | static unsigned tmaxflag; | ||
52 | static iopause_fd in; | ||
53 | |||
54 | static const char *replace = ""; | ||
55 | static char repl; | ||
56 | |||
57 | static 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; | ||
78 | static 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() | ||
86 | static void fatalx(char *m0) | ||
87 | { | ||
88 | bb_error_msg_and_die(FATAL"%s", m0); | ||
89 | } | ||
90 | static void warn(char *m0) { | ||
91 | bb_perror_msg(WARNING"%s", m0); | ||
92 | } | ||
93 | static void warn2(char *m0, char *m1) | ||
94 | { | ||
95 | bb_perror_msg(WARNING"%s: %s", m0, m1); | ||
96 | } | ||
97 | static void warnx(char *m0, char *m1) | ||
98 | { | ||
99 | bb_error_msg(WARNING"%s: %s", m0, m1); | ||
100 | } | ||
101 | static void pause_nomem(void) | ||
102 | { | ||
103 | bb_error_msg(PAUSE"out of memory"); sleep(3); | ||
104 | } | ||
105 | static void pause1cannot(char *m0) | ||
106 | { | ||
107 | bb_perror_msg(PAUSE"cannot %s", m0); sleep(3); | ||
108 | } | ||
109 | static void pause2cannot(char *m0, char *m1) | ||
110 | { | ||
111 | bb_perror_msg(PAUSE"cannot %s %s", m0, m1); | ||
112 | sleep(3); | ||
113 | } | ||
114 | |||
115 | static char* wstrdup(const char *str) | ||
116 | { | ||
117 | char *s; | ||
118 | while (!(s = strdup(str))) pause_nomem(); | ||
119 | return s; | ||
120 | } | ||
121 | |||
122 | static 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 | |||
178 | static 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 | |||
221 | static 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 | |||
255 | static 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 | |||
317 | static 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 | |||
379 | static 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 | |||
403 | static 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 | |||
563 | static 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 */ | ||
579 | static 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 | |||
657 | static 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 | |||
664 | static 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 | |||
679 | static 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 | |||
686 | static 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 | |||
693 | static 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 | |||
717 | int 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, ×tamp, &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 | } | ||