svlogd: extend usage text. +70 bytes
[platform/upstream/busybox.git] / runit / chpst.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8    1. Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10    2. Redistributions in binary form must reproduce the above copyright
11       notice, this list of conditions and the following disclaimer in the
12       documentation and/or other materials provided with the distribution.
13    3. The name of the author may not be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* Dependencies on runit_lib.c removed */
30
31 #include "libbb.h"
32
33 /*
34 Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
35
36 Only softlimit and chpst are taking options:
37
38 # common
39 -o N            Limit number of open files per process
40 -p N            Limit number of processes per uid
41 -m BYTES        Same as -d BYTES -s BYTES -l BYTES [-a BYTES]
42 -d BYTES        Limit data segment
43 -f BYTES        Limit output file sizes
44 -c BYTES        Limit core file size
45 # softlimit
46 -a BYTES        Limit total size of all segments
47 -s BYTES        Limit stack segment
48 -l BYTES        Limit locked memory size
49 -r BYTES        Limit resident set size
50 -t N            Limit CPU time
51 # chpst
52 -u USER[:GRP]   Set uid and gid
53 -U USER[:GRP]   Set $UID and $GID in environment
54 -e DIR          Set environment variables as specified by files in DIR
55 -/ DIR          Chroot to DIR
56 -n NICE         Add NICE to nice value
57 -v              Verbose
58 -P              Create new process group
59 -0 -1 -2        Close fd 0,1,2
60
61 Even though we accept all these options for both softlimit and chpst,
62 they are not to be advertised on their help texts.
63 We have enough problems with feature creep in other people's
64 software, don't want to add our own.
65
66 envdir, envuidgid, setuidgid take no options, but they reuse code which
67 handles -e, -U and -u.
68 */
69
70 enum {
71         OPT_a = (1 << 0) * ENABLE_SOFTLIMIT,
72         OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
73         OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
74         OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
75         OPT_l = (1 << 4) * ENABLE_SOFTLIMIT,
76         OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
77         OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
78         OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
79         OPT_r = (1 << 8) * ENABLE_SOFTLIMIT,
80         OPT_s = (1 << 9) * ENABLE_SOFTLIMIT,
81         OPT_t = (1 << 10) * ENABLE_SOFTLIMIT,
82         OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID),
83         OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID),
84         OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR),
85         OPT_root = (1 << 14) * ENABLE_CHPST,
86         OPT_n = (1 << 15) * ENABLE_CHPST,
87         OPT_v = (1 << 16) * ENABLE_CHPST,
88         OPT_P = (1 << 17) * ENABLE_CHPST,
89         OPT_0 = (1 << 18) * ENABLE_CHPST,
90         OPT_1 = (1 << 19) * ENABLE_CHPST,
91         OPT_2 = (1 << 20) * ENABLE_CHPST,
92 };
93
94 /* TODO: use recursive_action? */
95 static NOINLINE void edir(const char *directory_name)
96 {
97         int wdir;
98         DIR *dir;
99         struct dirent *d;
100         int fd;
101
102         wdir = xopen(".", O_RDONLY | O_NDELAY);
103         xchdir(directory_name);
104         dir = xopendir(".");
105         for (;;) {
106                 char buf[256];
107                 char *tail;
108                 int size;
109
110                 errno = 0;
111                 d = readdir(dir);
112                 if (!d) {
113                         if (errno)
114                                 bb_perror_msg_and_die("readdir %s",
115                                                 directory_name);
116                         break;
117                 }
118                 if (d->d_name[0] == '.')
119                         continue;
120                 fd = open(d->d_name, O_RDONLY | O_NDELAY);
121                 if (fd < 0) {
122                         if ((errno == EISDIR) && directory_name) {
123                                 if (option_mask32 & OPT_v)
124                                         bb_perror_msg("warning: %s/%s is a directory",
125                                                 directory_name, d->d_name);
126                                 continue;
127                         }
128                         bb_perror_msg_and_die("open %s/%s",
129                                                 directory_name, d->d_name);
130                 }
131                 size = full_read(fd, buf, sizeof(buf)-1);
132                 close(fd);
133                 if (size < 0)
134                         bb_perror_msg_and_die("read %s/%s",
135                                         directory_name, d->d_name);
136                 if (size == 0) {
137                         unsetenv(d->d_name);
138                         continue;
139                 }
140                 buf[size] = '\n';
141                 tail = strchr(buf, '\n');
142                 /* skip trailing whitespace */
143                 while (1) {
144                         *tail = '\0';
145                         tail--;
146                         if (tail < buf || !isspace(*tail))
147                                 break;
148                 }
149                 xsetenv(d->d_name, buf);
150         }
151         closedir(dir);
152         if (fchdir(wdir) == -1)
153                 bb_perror_msg_and_die("fchdir");
154         close(wdir);
155 }
156
157 static void limit(int what, long l)
158 {
159         struct rlimit r;
160
161         /* Never fails under Linux (except if you pass it bad arguments) */
162         getrlimit(what, &r);
163         if ((l < 0) || (l > r.rlim_max))
164                 r.rlim_cur = r.rlim_max;
165         else
166                 r.rlim_cur = l;
167         if (setrlimit(what, &r) == -1)
168                 bb_perror_msg_and_die("setrlimit");
169 }
170
171 int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
172 int chpst_main(int argc UNUSED_PARAM, char **argv)
173 {
174         struct bb_uidgid_t ugid;
175         char *set_user = set_user; /* for compiler */
176         char *env_user = env_user;
177         char *env_dir = env_dir;
178         char *root;
179         char *nicestr;
180         unsigned limita;
181         unsigned limitc;
182         unsigned limitd;
183         unsigned limitf;
184         unsigned limitl;
185         unsigned limitm;
186         unsigned limito;
187         unsigned limitp;
188         unsigned limitr;
189         unsigned limits;
190         unsigned limitt;
191         unsigned opt;
192
193         if ((ENABLE_CHPST && applet_name[0] == 'c')
194          || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
195         ) {
196                 // FIXME: can we live with int-sized limits?
197                 // can we live with 40000 days?
198                 // if yes -> getopt converts strings to numbers for us
199                 opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
200                 opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
201                         IF_CHPST("/:n:vP012"),
202                         &limita, &limitc, &limitd, &limitf, &limitl,
203                         &limitm, &limito, &limitp, &limitr, &limits, &limitt,
204                         &set_user, &env_user, &env_dir
205                         IF_CHPST(, &root, &nicestr));
206                 argv += optind;
207                 if (opt & OPT_m) { // -m means -asld
208                         limita = limits = limitl = limitd = limitm;
209                         opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
210                 }
211         } else {
212                 option_mask32 = opt = 0;
213                 argv++;
214                 if (!*argv)
215                         bb_show_usage();
216         }
217
218         // envdir?
219         if (ENABLE_ENVDIR && applet_name[3] == 'd') {
220                 env_dir = *argv++;
221                 opt |= OPT_e;
222         }
223
224         // setuidgid?
225         if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
226                 set_user = *argv++;
227                 opt |= OPT_u;
228         }
229
230         // envuidgid?
231         if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
232                 env_user = *argv++;
233                 opt |= OPT_U;
234         }
235
236         // we must have PROG [ARGS]
237         if (!*argv)
238                 bb_show_usage();
239
240         // set limits
241         if (opt & OPT_d) {
242 #ifdef RLIMIT_DATA
243                 limit(RLIMIT_DATA, limitd);
244 #else
245                 if (opt & OPT_v)
246                         bb_error_msg("system does not support RLIMIT_%s",
247                                 "DATA");
248 #endif
249         }
250         if (opt & OPT_s) {
251 #ifdef RLIMIT_STACK
252                 limit(RLIMIT_STACK, limits);
253 #else
254                 if (opt & OPT_v)
255                         bb_error_msg("system does not support RLIMIT_%s",
256                                 "STACK");
257 #endif
258         }
259         if (opt & OPT_l) {
260 #ifdef RLIMIT_MEMLOCK
261                 limit(RLIMIT_MEMLOCK, limitl);
262 #else
263                 if (opt & OPT_v)
264                         bb_error_msg("system does not support RLIMIT_%s",
265                                 "MEMLOCK");
266 #endif
267         }
268         if (opt & OPT_a) {
269 #ifdef RLIMIT_VMEM
270                 limit(RLIMIT_VMEM, limita);
271 #else
272 #ifdef RLIMIT_AS
273                 limit(RLIMIT_AS, limita);
274 #else
275                 if (opt & OPT_v)
276                         bb_error_msg("system does not support RLIMIT_%s",
277                                 "VMEM");
278 #endif
279 #endif
280         }
281         if (opt & OPT_o) {
282 #ifdef RLIMIT_NOFILE
283                 limit(RLIMIT_NOFILE, limito);
284 #else
285 #ifdef RLIMIT_OFILE
286                 limit(RLIMIT_OFILE, limito);
287 #else
288                 if (opt & OPT_v)
289                         bb_error_msg("system does not support RLIMIT_%s",
290                                 "NOFILE");
291 #endif
292 #endif
293         }
294         if (opt & OPT_p) {
295 #ifdef RLIMIT_NPROC
296                 limit(RLIMIT_NPROC, limitp);
297 #else
298                 if (opt & OPT_v)
299                         bb_error_msg("system does not support RLIMIT_%s",
300                                 "NPROC");
301 #endif
302         }
303         if (opt & OPT_f) {
304 #ifdef RLIMIT_FSIZE
305                 limit(RLIMIT_FSIZE, limitf);
306 #else
307                 if (opt & OPT_v)
308                         bb_error_msg("system does not support RLIMIT_%s",
309                                 "FSIZE");
310 #endif
311         }
312         if (opt & OPT_c) {
313 #ifdef RLIMIT_CORE
314                 limit(RLIMIT_CORE, limitc);
315 #else
316                 if (opt & OPT_v)
317                         bb_error_msg("system does not support RLIMIT_%s",
318                                 "CORE");
319 #endif
320         }
321         if (opt & OPT_r) {
322 #ifdef RLIMIT_RSS
323                 limit(RLIMIT_RSS, limitr);
324 #else
325                 if (opt & OPT_v)
326                         bb_error_msg("system does not support RLIMIT_%s",
327                                 "RSS");
328 #endif
329         }
330         if (opt & OPT_t) {
331 #ifdef RLIMIT_CPU
332                 limit(RLIMIT_CPU, limitt);
333 #else
334                 if (opt & OPT_v)
335                         bb_error_msg("system does not support RLIMIT_%s",
336                                 "CPU");
337 #endif
338         }
339
340         if (opt & OPT_P)
341                 setsid();
342
343         if (opt & OPT_e)
344                 edir(env_dir);
345
346         // FIXME: chrooted jail must have /etc/passwd if we move this after chroot!
347         // OTOH chroot fails for non-roots!
348         // SOLUTION: cache uid/gid before chroot, apply uid/gid after
349         if (opt & OPT_U) {
350                 xget_uidgid(&ugid, env_user);
351                 xsetenv("GID", utoa(ugid.gid));
352                 xsetenv("UID", utoa(ugid.uid));
353         }
354
355         if (opt & OPT_u) {
356                 xget_uidgid(&ugid, set_user);
357         }
358
359         if (opt & OPT_root) {
360                 xchdir(root);
361                 xchroot(".");
362         }
363
364         if (opt & OPT_u) {
365                 if (setgroups(1, &ugid.gid) == -1)
366                         bb_perror_msg_and_die("setgroups");
367                 xsetgid(ugid.gid);
368                 xsetuid(ugid.uid);
369         }
370
371         if (opt & OPT_n) {
372                 errno = 0;
373                 if (nice(xatoi(nicestr)) == -1)
374                         bb_perror_msg_and_die("nice");
375         }
376
377         if (opt & OPT_0)
378                 close(STDIN_FILENO);
379         if (opt & OPT_1)
380                 close(STDOUT_FILENO);
381         if (opt & OPT_2)
382                 close(STDERR_FILENO);
383
384         BB_EXECVP_or_die(argv);
385 }