chpst: fix "env directory" parsing to not strip everything
[oweals/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 #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 struct globals {
43         char *set_user;
44         char *env_user;
45         const char *env_dir;
46         const char *root;
47         long limitd; /* limitX are initialized to -2 */
48         long limits;
49         long limitl;
50         long limita;
51         long limito;
52         long limitp;
53         long limitf;
54         long limitc;
55         long limitr;
56         long limitt;
57         int nicelvl;
58 };
59 #define G (*(struct globals*)&bb_common_bufsiz1)
60 #define set_user (G.set_user)
61 #define env_user (G.env_user)
62 #define env_dir  (G.env_dir )
63 #define root     (G.root    )
64 #define limitd   (G.limitd  )
65 #define limits   (G.limits  )
66 #define limitl   (G.limitl  )
67 #define limita   (G.limita  )
68 #define limito   (G.limito  )
69 #define limitp   (G.limitp  )
70 #define limitf   (G.limitf  )
71 #define limitc   (G.limitc  )
72 #define limitr   (G.limitr  )
73 #define limitt   (G.limitt  )
74 #define nicelvl  (G.nicelvl )
75 #define INIT_G() do { \
76         long *p = &limitd; \
77         do *p++ = -2; while (p <= &limitt); \
78 } while (0)
79
80 static void suidgid(char *user)
81 {
82         struct bb_uidgid_t ugid;
83
84         if (!get_uidgid(&ugid, user, 1)) {
85                 bb_error_msg_and_die("unknown user/group: %s", user);
86         }
87         if (setgroups(1, &ugid.gid) == -1)
88                 bb_perror_msg_and_die("setgroups");
89         xsetgid(ugid.gid);
90         xsetuid(ugid.uid);
91 }
92
93 static void euidgid(char *user)
94 {
95         struct bb_uidgid_t ugid;
96
97         if (!get_uidgid(&ugid, user, 1)) {
98                 bb_error_msg_and_die("unknown user/group: %s", user);
99         }
100         xsetenv("GID", utoa(ugid.gid));
101         xsetenv("UID", utoa(ugid.uid));
102 }
103
104 static void edir(const char *directory_name)
105 {
106         int wdir;
107         DIR *dir;
108         struct dirent *d;
109         int fd;
110
111         wdir = xopen(".", O_RDONLY | O_NDELAY);
112         xchdir(directory_name);
113         dir = opendir(".");
114         if (!dir)
115                 bb_perror_msg_and_die("opendir %s", directory_name);
116         for (;;) {
117                 char buf[256];
118                 char *tail;
119                 int size;
120
121                 errno = 0;
122                 d = readdir(dir);
123                 if (!d) {
124                         if (errno)
125                                 bb_perror_msg_and_die("readdir %s",
126                                                 directory_name);
127                         break;
128                 }
129                 if (d->d_name[0] == '.')
130                         continue;
131                 fd = open(d->d_name, O_RDONLY | O_NDELAY);
132                 if (fd < 0) {
133                         if ((errno == EISDIR) && env_dir) {
134                                 if (OPT_verbose)
135                                         bb_perror_msg("warning: %s/%s is a directory",
136                                                 directory_name, d->d_name);
137                                 continue;
138                         } else
139                                 bb_perror_msg_and_die("open %s/%s",
140                                                 directory_name, d->d_name);
141                 }
142                 size = full_read(fd, buf, sizeof(buf)-1);
143                 close(fd);
144                 if (size < 0)
145                         bb_perror_msg_and_die("read %s/%s",
146                                         directory_name, d->d_name);
147                 if (size == 0) {
148                         unsetenv(d->d_name);
149                         continue;
150                 }
151                 buf[size] = '\n';
152                 tail = strchr(buf, '\n');
153                 /* skip trailing whitespace */
154                 while (1) {
155                         *tail = '\0';
156                         tail--;
157                         if (tail < buf || !isspace(*tail))
158                                 break;
159                 }
160                 xsetenv(d->d_name, buf);
161         }
162         closedir(dir);
163         if (fchdir(wdir) == -1)
164                 bb_perror_msg_and_die("fchdir");
165         close(wdir);
166 }
167
168 static void limit(int what, long l)
169 {
170         struct rlimit r;
171
172         /* Never fails under Linux (except if you pass it bad arguments) */
173         getrlimit(what, &r);
174         if ((l < 0) || (l > r.rlim_max))
175                 r.rlim_cur = r.rlim_max;
176         else
177                 r.rlim_cur = l;
178         if (setrlimit(what, &r) == -1)
179                 bb_perror_msg_and_die("setrlimit");
180 }
181
182 static void slimit(void)
183 {
184         if (limitd >= -1) {
185 #ifdef RLIMIT_DATA
186                 limit(RLIMIT_DATA, limitd);
187 #else
188                 if (OPT_verbose)
189                         bb_error_msg("system does not support RLIMIT_%s",
190                                 "DATA");
191 #endif
192         }
193         if (limits >= -1) {
194 #ifdef RLIMIT_STACK
195                 limit(RLIMIT_STACK, limits);
196 #else
197                 if (OPT_verbose)
198                         bb_error_msg("system does not support RLIMIT_%s",
199                                 "STACK");
200 #endif
201         }
202         if (limitl >= -1) {
203 #ifdef RLIMIT_MEMLOCK
204                 limit(RLIMIT_MEMLOCK, limitl);
205 #else
206                 if (OPT_verbose)
207                         bb_error_msg("system does not support RLIMIT_%s",
208                                 "MEMLOCK");
209 #endif
210         }
211         if (limita >= -1) {
212 #ifdef RLIMIT_VMEM
213                 limit(RLIMIT_VMEM, limita);
214 #else
215 #ifdef RLIMIT_AS
216                 limit(RLIMIT_AS, limita);
217 #else
218                 if (OPT_verbose)
219                         bb_error_msg("system does not support RLIMIT_%s",
220                                 "VMEM");
221 #endif
222 #endif
223         }
224         if (limito >= -1) {
225 #ifdef RLIMIT_NOFILE
226                 limit(RLIMIT_NOFILE, limito);
227 #else
228 #ifdef RLIMIT_OFILE
229                 limit(RLIMIT_OFILE, limito);
230 #else
231                 if (OPT_verbose)
232                         bb_error_msg("system does not support RLIMIT_%s",
233                                 "NOFILE");
234 #endif
235 #endif
236         }
237         if (limitp >= -1) {
238 #ifdef RLIMIT_NPROC
239                 limit(RLIMIT_NPROC, limitp);
240 #else
241                 if (OPT_verbose)
242                         bb_error_msg("system does not support RLIMIT_%s",
243                                 "NPROC");
244 #endif
245         }
246         if (limitf >= -1) {
247 #ifdef RLIMIT_FSIZE
248                 limit(RLIMIT_FSIZE, limitf);
249 #else
250                 if (OPT_verbose)
251                         bb_error_msg("system does not support RLIMIT_%s",
252                                 "FSIZE");
253 #endif
254         }
255         if (limitc >= -1) {
256 #ifdef RLIMIT_CORE
257                 limit(RLIMIT_CORE, limitc);
258 #else
259                 if (OPT_verbose)
260                         bb_error_msg("system does not support RLIMIT_%s",
261                                 "CORE");
262 #endif
263         }
264         if (limitr >= -1) {
265 #ifdef RLIMIT_RSS
266                 limit(RLIMIT_RSS, limitr);
267 #else
268                 if (OPT_verbose)
269                         bb_error_msg("system does not support RLIMIT_%s",
270                                 "RSS");
271 #endif
272         }
273         if (limitt >= -1) {
274 #ifdef RLIMIT_CPU
275                 limit(RLIMIT_CPU, limitt);
276 #else
277                 if (OPT_verbose)
278                         bb_error_msg("system does not support RLIMIT_%s",
279                                 "CPU");
280 #endif
281         }
282 }
283
284 /* argv[0] */
285 static void setuidgid(int, char **) ATTRIBUTE_NORETURN;
286 static void envuidgid(int, char **) ATTRIBUTE_NORETURN;
287 static void envdir(int, char **) ATTRIBUTE_NORETURN;
288 static void softlimit(int, char **) ATTRIBUTE_NORETURN;
289
290 int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
291 int chpst_main(int argc ATTRIBUTE_UNUSED, char **argv)
292 {
293         INIT_G();
294
295         if (applet_name[3] == 'd') envdir(argc, argv);
296         if (applet_name[1] == 'o') softlimit(argc, argv);
297         if (applet_name[0] == 's') setuidgid(argc, argv);
298         if (applet_name[0] == 'e') envuidgid(argc, argv);
299         // otherwise we are chpst
300
301         {
302                 char *m,*d,*o,*p,*f,*c,*r,*t,*n;
303                 getopt32(argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
304                                 &set_user,&env_user,&env_dir,
305                                 &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
306                 // if (option_mask32 & 0x1) // -u
307                 // if (option_mask32 & 0x2) // -U
308                 // if (option_mask32 & 0x4) // -e
309                 if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
310                 if (option_mask32 & 0x10) limitd = xatoul(d); // -d
311                 if (option_mask32 & 0x20) limito = xatoul(o); // -o
312                 if (option_mask32 & 0x40) limitp = xatoul(p); // -p
313                 if (option_mask32 & 0x80) limitf = xatoul(f); // -f
314                 if (option_mask32 & 0x100) limitc = xatoul(c); // -c
315                 if (option_mask32 & 0x200) limitr = xatoul(r); // -r
316                 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
317                 // if (option_mask32 & 0x800) // -/
318                 if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
319                 // The below consts should match #defines at top!
320                 //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
321                 //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
322                 //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
323                 //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
324                 //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
325         }
326         argv += optind;
327         if (!argv || !*argv) bb_show_usage();
328
329         if (OPT_pgrp) setsid();
330         if (env_dir) edir(env_dir);
331         if (root) {
332                 xchdir(root);
333                 xchroot(".");
334         }
335         slimit();
336         if (nicelvl) {
337                 errno = 0;
338                 if (nice(nicelvl) == -1)
339                         bb_perror_msg_and_die("nice");
340         }
341         if (env_user) euidgid(env_user);
342         if (set_user) suidgid(set_user);
343         if (OPT_nostdin) close(0);
344         if (OPT_nostdout) close(1);
345         if (OPT_nostderr) close(2);
346         BB_EXECVP(argv[0], argv);
347         bb_perror_msg_and_die("exec %s", argv[0]);
348 }
349
350 static void setuidgid(int argc ATTRIBUTE_UNUSED, char **argv)
351 {
352         const char *account;
353
354         account = *++argv;
355         if (!account) bb_show_usage();
356         if (!*++argv) bb_show_usage();
357         suidgid((char*)account);
358         BB_EXECVP(argv[0], argv);
359         bb_perror_msg_and_die("exec %s", argv[0]);
360 }
361
362 static void envuidgid(int argc ATTRIBUTE_UNUSED, char **argv)
363 {
364         const char *account;
365
366         account = *++argv;
367         if (!account) bb_show_usage();
368         if (!*++argv) bb_show_usage();
369         euidgid((char*)account);
370         BB_EXECVP(argv[0], argv);
371         bb_perror_msg_and_die("exec %s", argv[0]);
372 }
373
374 static void envdir(int argc ATTRIBUTE_UNUSED, char **argv)
375 {
376         const char *dir;
377
378         dir = *++argv;
379         if (!dir) bb_show_usage();
380         if (!*++argv) bb_show_usage();
381         edir(dir);
382         BB_EXECVP(argv[0], argv);
383         bb_perror_msg_and_die("exec %s", argv[0]);
384 }
385
386 static void softlimit(int argc ATTRIBUTE_UNUSED, char **argv)
387 {
388         char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
389         getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:",
390                         &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
391         if (option_mask32 & 0x001) limita = xatoul(a); // -a
392         if (option_mask32 & 0x002) limitc = xatoul(c); // -c
393         if (option_mask32 & 0x004) limitd = xatoul(d); // -d
394         if (option_mask32 & 0x008) limitf = xatoul(f); // -f
395         if (option_mask32 & 0x010) limitl = xatoul(l); // -l
396         if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
397         if (option_mask32 & 0x040) limito = xatoul(o); // -o
398         if (option_mask32 & 0x080) limitp = xatoul(p); // -p
399         if (option_mask32 & 0x100) limitr = xatoul(r); // -r
400         if (option_mask32 & 0x200) limits = xatoul(s); // -s
401         if (option_mask32 & 0x400) limitt = xatoul(t); // -t
402         argv += optind;
403         if (!argv[0]) bb_show_usage();
404         slimit();
405         BB_EXECVP(argv[0], argv);
406         bb_perror_msg_and_die("exec %s", argv[0]);
407 }