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