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