2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Copyright (c) 2006, 2007 Guillaume Chazarain <guichaz@yahoo.fr>
22 #include <sys/resource.h>
23 #include <sys/types.h>
37 * Copied from linux-2.6/include/linux/sched.h
38 * Compile on old libc, run on new kernel :-)
40 #define CLONE_NEWNS 0x00020000 /* New namespace */
41 #define CLONE_NEWUTS 0x04000000 /* New utsname */
42 #define CLONE_NEWIPC 0x08000000 /* New ipcs */
43 #define CLONE_NEWUSER 0x10000000 /* New user namespace */
44 #define CLONE_NEWPID 0x20000000 /* New pid namespace */
45 #define CLONE_NEWNET 0x40000000 /* New network namespace */
50 * We find out the UID/GID infos before the chroot as we may need /etc/passwd.
51 * The results are stored in this struct.
61 * The temporary stack for the cloned process
63 #define STACK_SIZE (1 << 20)
64 static char stack
[STACK_SIZE
];
67 * Get the clone(2) flags from the command line arguments
69 static int get_flags(struct gengetopt_args_info
*args_info
)
74 if (args_info
->mount_flag
)
77 if (args_info
->uname_flag
)
78 flags
|= CLONE_NEWUTS
;
80 if (args_info
->ipc_flag
)
81 flags
|= CLONE_NEWIPC
;
83 if (args_info
->userns_flag
)
84 flags
|= CLONE_NEWUSER
;
86 if (args_info
->pidns_flag
)
87 flags
|= CLONE_NEWPID
;
89 if (args_info
->netns_flag
)
90 flags
|= CLONE_NEWNET
;
92 for (i
= 0; i
< args_info
->mask_given
; i
++)
93 flags
|= args_info
->mask_arg
[i
];
99 * Wrapper for malloc, exit instead of returning NULL
101 static void *xmalloc(int size
)
103 void *res
= malloc(size
);
112 * Do a chroot() if requested. Do also a chdir() to prevent the simplest
113 * form a chroot escape.
115 static void perform_chroot(struct gengetopt_args_info
*ggo
)
117 if (ggo
->chroot_given
) {
118 if (chroot(ggo
->chroot_arg
) < 0) {
123 if (chdir("/") < 0) {
124 perror("chdir(\"/\")");
131 * Find the UID associated to a username
133 static uid_t
name_to_uid(const char *name
)
138 pass
= getpwnam(name
);
144 fprintf(stderr
, "%s: user not found\n", name
);
152 * Find the GID associated to a group
154 static gid_t
name_to_gid(const char *name
)
159 grp
= getgrnam(name
);
165 fprintf(stderr
, "%s: group not found\n", name
);
173 * Transform the command line args (ggo) into our struct (user)
175 static void get_user_infos(struct user_info
*user
,
176 struct gengetopt_args_info
*ggo
)
178 size_t nr_groups
= ggo
->gid_given
+ ggo
->groupname_given
;
180 if (ggo
->uid_given
&& ggo
->username_given
) {
181 fputs("--uid and --username are mutually exclusive\n", stderr
);
186 user
->uid
= ggo
->uid_arg
;
187 else if (ggo
->username_given
)
188 user
->uid
= name_to_uid(ggo
->username_arg
);
193 user
->groups
= xmalloc(nr_groups
* sizeof(gid_t
));
198 if (ggo
->gid_given
) {
199 memcpy(user
->groups
, ggo
->gid_arg
,
200 ggo
->gid_given
* sizeof(gid_t
));
201 user
->nr_groups
= ggo
->gid_given
;
204 if (ggo
->groupname_given
) {
206 for (i
= 0; i
< ggo
->groupname_given
; i
++) {
207 gid_t gid
= name_to_gid(ggo
->groupname_arg
[i
]);
208 user
->groups
[user
->nr_groups
] = gid
;
214 user
->gid
= user
->groups
[0];
216 user
->gid
= user
->uid
;
217 if (user
->uid
!= -1) {
219 user
->groups
= &user
->uid
;
225 * Change the UID and GID if requested.
227 static void change_id(struct user_info
*user
)
229 if (user
->nr_groups
) {
230 if (setgroups(user
->nr_groups
, user
->groups
) < 0) {
236 if (user
->gid
!= -1) {
237 if (setgid(user
->gid
) < 0) {
243 if (user
->uid
!= -1) {
244 if (setuid(user
->uid
) < 0) {
254 static void daemonize(void)
272 perror("chdir(\"/\")");
274 fd
= open("/dev/null", O_RDWR
, 0);
276 if (dup2(fd
, STDIN_FILENO
) < 0)
277 perror("dup2(fd, STDIN_FILENO)");
278 if (dup2(fd
, STDOUT_FILENO
) < 0)
279 perror("dup2(fd, STDOUT_FILENO)");
280 if (dup2(fd
, STDERR_FILENO
) < 0)
281 perror("dup2(fd, STDERR_FILENO)");
282 if (fd
> STDERR_FILENO
)
285 perror("open(\"/dev/null\")");
289 * Configure a (posssibly RT) scheduling policy
291 static void set_scheduler(struct gengetopt_args_info
*ggo
)
298 {"fifo", SCHED_FIFO
},
300 {"other", SCHED_OTHER
},
301 {"normal", SCHED_OTHER
}
304 struct sched_param param
;
306 if (ggo
->sched_given
) {
308 int policy_found
= 0;
311 for (i
= 0; i
< sizeof(policies
) / sizeof(policies
[0]); i
++)
312 if (!strcasecmp(ggo
->sched_arg
, policies
[i
].name
)) {
313 policy
= policies
[i
].value
;
320 policy
= strtol(ggo
->sched_arg
, &end
, 0);
321 if (ggo
->sched_arg
[0] == '\0' || end
[0] != '\0') {
323 "Unknown scheduling policy: %s\n",
329 if (ggo
->schedprio_given
)
330 param
.sched_priority
= ggo
->schedprio_arg
;
332 param
.sched_priority
= sched_get_priority_min(policy
);
333 if (param
.sched_priority
< 0) {
334 perror("sched_get_priority_min");
339 if (sched_setscheduler(0, policy
, ¶m
) < 0) {
340 perror("sched_setscheduler");
343 } else if (ggo
->schedprio_given
) {
344 param
.sched_priority
= ggo
->schedprio_arg
;
345 if (sched_setparam(0, ¶m
) < 0) {
346 perror("sched_setparam");
352 static pid_t child_pid
;
354 static void kill_child()
357 kill(child_pid
, SIGKILL
);
360 static void deadly_signal_handler(int sig
)
366 * If the parent is killed, it must kill its child too
368 static void install_signals_handlers(void)
370 int signals
[] = { SIGHUP
, SIGINT
, SIGQUIT
, SIGTERM
};
373 for (i
= 0; i
< sizeof(signals
) / sizeof(signals
[0]); i
++)
374 signal(signals
[i
], deadly_signal_handler
);
378 * Exit when a SIGCHLD signals that the child has exited
380 static void sig_chld_handler(int sig
, siginfo_t
* info
, void *data
)
386 code
= info
->si_status
;
387 if (info
->si_code
& CLD_KILLED
) {
396 * Configure the SIGCHLD handler to detect the child termination and propagate
399 static void install_sigchld_handler(struct gengetopt_args_info
*ggo
)
401 struct sigaction act
;
403 act
.sa_sigaction
= sig_chld_handler
;
404 act
.sa_flags
= SA_SIGINFO
| SA_NOCLDSTOP
;
405 sigemptyset(&act
.sa_mask
);
407 if (sigaction(SIGCHLD
, &act
, NULL
) < 0) {
414 * Check double parameters to be used as struct timespec
416 static void check_double_as_timespec(double seconds
)
418 time_t as_integer
= (time_t) seconds
;
419 double delta
= seconds
- as_integer
;
421 if (seconds
<= 0.0 || as_integer
< 0) {
422 fprintf(stderr
, "Invalid (negative?) timeout: %lf\n", seconds
);
426 if (delta
< 0.0 || delta
>= 1.0) {
427 fprintf(stderr
, "Invalid (overflow?) timeout: %lf\n", seconds
);
433 * Kill the child after the specified delay
435 static void wait_to_kill(double seconds
, int sig
)
437 struct timespec sleep_time
;
439 sleep_time
.tv_sec
= (time_t) seconds
;
440 sleep_time
.tv_nsec
= (long)((seconds
- sleep_time
.tv_sec
) * 1e9
);
442 if (nanosleep(&sleep_time
, NULL
) < 0) {
447 if (kill(child_pid
, sig
) < 0) {
454 * Kill the child according to the two possible delays
456 static void watchdog(struct gengetopt_args_info
*ggo
)
461 if (!ggo
->timeout_term_given
&& !ggo
->timeout_kill_given
)
464 if (ggo
->daemonize_flag
) {
465 fputs("--timeout-XXXX is incompatible with --daemonize\n",
470 if (!ggo
->timeout_term_given
|| (ggo
->timeout_kill_given
&&
471 ggo
->timeout_kill_arg
<
472 ggo
->timeout_term_arg
)) {
473 /* SIGKILL before SIGTERM? Why not ;-) */
475 delay
= ggo
->timeout_kill_arg
;
478 delay
= ggo
->timeout_term_arg
;
481 wait_to_kill(delay
, sig
);
483 if (ggo
->timeout_term_given
!= ggo
->timeout_kill_given
)
486 delay
= ggo
->timeout_kill_arg
- ggo
->timeout_term_arg
;
490 /* The other signal */
491 sig
= SIGKILL
+ SIGTERM
- sig
;
492 wait_to_kill(delay
, sig
);
496 * The new process in its own namespace can now execute the command passed as
497 * argument or a shell
499 static int child_process(void *ggo_arg
)
501 struct gengetopt_args_info
*ggo
= ggo_arg
;
502 struct user_info user
;
507 if (setpriority(PRIO_PROCESS
, 0, ggo
->nice_arg
) < 0) {
508 perror("setpriority");
512 get_user_infos(&user
, ggo
);
516 if (ggo
->daemonize_flag
)
519 if (ggo
->alarm_given
)
520 alarm(ggo
->alarm_arg
);
522 if (ggo
->inputs_num
) {
523 char **argv
= xmalloc(sizeof(char *) * (ggo
->inputs_num
+ 1));
524 memcpy(argv
, ggo
->inputs
, ggo
->inputs_num
* sizeof(char *));
525 argv
[ggo
->inputs_num
] = NULL
;
526 execvp(argv
[0], argv
);
529 char *argv
[] = { NULL
, NULL
};
530 char *shell
= getenv("SHELL");
534 execv(argv
[0], argv
);
541 int main(int argc
, char *argv
[])
543 struct gengetopt_args_info args_info
;
548 ret
= cmdline_parser(argc
, argv
, &args_info
);
551 if (argc
- args_info
.inputs_num
<= 1) {
552 fputs("No flag specified, exiting\n", stderr
);
556 if (args_info
.alarm_given
&&
557 (args_info
.alarm_arg
< 0 || args_info
.alarm_arg
> 65535)) {
559 "The alarm value must be between 0 and 65535 got: %d\n",
560 args_info
.alarm_arg
);
564 if (args_info
.timeout_term_given
)
565 check_double_as_timespec(args_info
.timeout_term_arg
);
566 if (args_info
.timeout_kill_given
)
567 check_double_as_timespec(args_info
.timeout_kill_arg
);
569 flags
= get_flags(&args_info
);
570 fork_needed
= flags
!= 0 || args_info
.timeout_term_given
||
571 args_info
.timeout_kill_given
;
575 install_sigchld_handler(&args_info
);
576 install_signals_handlers();
578 child_pid
= clone(child_process
, &stack
[STACK_SIZE
- 1], flags
,
585 watchdog(&args_info
);
588 ret
= child_process(&args_info
);