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 */
48 * We find out the UID/GID infos before the chroot as we may need /etc/passwd.
49 * The results are stored in this struct.
59 * The temporary stack for the cloned process
61 #define STACK_SIZE (1 << 20)
62 static char stack
[STACK_SIZE
];
65 * Get the clone(2) flags from the command line arguments
67 static int get_flags(struct gengetopt_args_info
*args_info
)
72 if (args_info
->mount_flag
)
75 if (args_info
->uname_flag
)
76 flags
|= CLONE_NEWUTS
;
78 if (args_info
->ipc_flag
)
79 flags
|= CLONE_NEWIPC
;
81 if (args_info
->userns_flag
)
82 flags
|= CLONE_NEWUSER
;
84 for (i
= 0; i
< args_info
->mask_given
; i
++)
85 flags
|= args_info
->mask_arg
[i
];
91 * Wrapper for malloc, exit instead of returning NULL
93 static void *xmalloc(int size
)
95 void *res
= malloc(size
);
104 * Do a chroot() if requested. Do also a chdir() to prevent the simplest
105 * form a chroot escape.
107 static void perform_chroot(struct gengetopt_args_info
*ggo
)
109 if (ggo
->chroot_given
) {
110 if (chroot(ggo
->chroot_arg
) < 0) {
115 if (chdir("/") < 0) {
116 perror("chdir(\"/\")");
123 * Find the UID associated to a username
125 static uid_t
name_to_uid(const char *name
)
130 pass
= getpwnam(name
);
136 fprintf(stderr
, "%s: user not found\n", name
);
144 * Find the GID associated to a group
146 static gid_t
name_to_gid(const char *name
)
151 grp
= getgrnam(name
);
157 fprintf(stderr
, "%s: group not found\n", name
);
165 * Transform the command line args (ggo) into our struct (user)
167 static void get_user_infos(struct user_info
*user
,
168 struct gengetopt_args_info
*ggo
)
170 size_t nr_groups
= ggo
->gid_given
+ ggo
->groupname_given
;
172 if (ggo
->uid_given
&& ggo
->username_given
) {
173 fputs("--uid and --username are mutually exclusive\n", stderr
);
178 user
->uid
= ggo
->uid_arg
;
179 else if (ggo
->username_given
)
180 user
->uid
= name_to_uid(ggo
->username_arg
);
185 user
->groups
= xmalloc(nr_groups
* sizeof(gid_t
));
190 if (ggo
->gid_given
) {
191 memcpy(user
->groups
, ggo
->gid_arg
,
192 ggo
->gid_given
* sizeof(gid_t
));
193 user
->nr_groups
= ggo
->gid_given
;
196 if (ggo
->groupname_given
) {
198 for (i
= 0; i
< ggo
->groupname_given
; i
++) {
199 gid_t gid
= name_to_gid(ggo
->groupname_arg
[i
]);
200 user
->groups
[user
->nr_groups
] = gid
;
206 user
->gid
= user
->groups
[0];
208 user
->gid
= user
->uid
;
209 if (user
->uid
!= -1) {
211 user
->groups
= &user
->uid
;
217 * Change the UID and GID if requested.
219 static void change_id(struct user_info
*user
)
221 if (user
->nr_groups
) {
222 if (setgroups(user
->nr_groups
, user
->groups
) < 0) {
228 if (user
->gid
!= -1) {
229 if (setgid(user
->gid
) < 0) {
235 if (user
->uid
!= -1) {
236 if (setuid(user
->uid
) < 0) {
246 static void daemonize(void)
264 perror("chdir(\"/\")");
266 fd
= open("/dev/null", O_RDWR
, 0);
268 if (dup2(fd
, STDIN_FILENO
) < 0)
269 perror("dup2(fd, STDIN_FILENO)");
270 if (dup2(fd
, STDOUT_FILENO
) < 0)
271 perror("dup2(fd, STDOUT_FILENO)");
272 if (dup2(fd
, STDERR_FILENO
) < 0)
273 perror("dup2(fd, STDERR_FILENO)");
274 if (fd
> STDERR_FILENO
)
277 perror("open(\"/dev/null\")");
281 * Configure a (posssibly RT) scheduling policy
283 static void set_scheduler(struct gengetopt_args_info
*ggo
)
290 {"fifo", SCHED_FIFO
},
292 {"other", SCHED_OTHER
},
293 {"normal", SCHED_OTHER
}
296 struct sched_param param
;
298 if (ggo
->sched_given
) {
300 int policy_found
= 0;
303 for (i
= 0; i
< sizeof(policies
) / sizeof(policies
[0]); i
++)
304 if (!strcasecmp(ggo
->sched_arg
, policies
[i
].name
)) {
305 policy
= policies
[i
].value
;
312 policy
= strtol(ggo
->sched_arg
, &end
, 0);
313 if (ggo
->sched_arg
[0] == '\0' || end
[0] != '\0') {
315 "Unknown scheduling policy: %s\n",
321 if (ggo
->schedprio_given
)
322 param
.sched_priority
= ggo
->schedprio_arg
;
324 param
.sched_priority
= sched_get_priority_min(policy
);
325 if (param
.sched_priority
< 0) {
326 perror("sched_get_priority_min");
331 if (sched_setscheduler(0, policy
, ¶m
) < 0) {
332 perror("sched_setscheduler");
335 } else if (ggo
->schedprio_given
) {
336 param
.sched_priority
= ggo
->schedprio_arg
;
337 if (sched_setparam(0, ¶m
) < 0) {
338 perror("sched_setparam");
344 static pid_t child_pid
;
346 static void kill_child()
349 kill(child_pid
, SIGKILL
);
352 static void deadly_signal_handler(int sig
)
358 * If the parent is killed, it must kill its child too
360 static void install_signals_handlers(void)
362 int signals
[] = { SIGHUP
, SIGINT
, SIGQUIT
, SIGTERM
};
365 for (i
= 0; i
< sizeof(signals
) / sizeof(signals
[0]); i
++)
366 signal(signals
[i
], deadly_signal_handler
);
370 * Exit when a SIGCHLD signals that the child has exited
372 static void sig_chld_handler(int sig
, siginfo_t
* info
, void *data
)
378 code
= info
->si_status
;
379 if (info
->si_code
& CLD_KILLED
) {
388 * Configure the SIGCHLD handler to detect the child termination and propagate
391 static void install_sigchld_handler(struct gengetopt_args_info
*ggo
)
393 struct sigaction act
;
395 act
.sa_sigaction
= sig_chld_handler
;
396 act
.sa_flags
= SA_SIGINFO
| SA_NOCLDSTOP
;
397 sigemptyset(&act
.sa_mask
);
399 if (sigaction(SIGCHLD
, &act
, NULL
) < 0) {
406 * Check double parameters to be used as struct timespec
408 static void check_double_as_timespec(double seconds
)
410 time_t as_integer
= (time_t) seconds
;
411 double delta
= seconds
- as_integer
;
413 if (seconds
<= 0.0 || as_integer
< 0) {
414 fprintf(stderr
, "Invalid (negative?) timeout: %lf\n", seconds
);
418 if (delta
< 0.0 || delta
>= 1.0) {
419 fprintf(stderr
, "Invalid (overflow?) timeout: %lf\n", seconds
);
425 * Kill the child after the specified delay
427 static void wait_to_kill(double seconds
, int sig
)
429 struct timespec sleep_time
;
431 sleep_time
.tv_sec
= (time_t) seconds
;
432 sleep_time
.tv_nsec
= (long)((seconds
- sleep_time
.tv_sec
) * 1e9
);
434 if (nanosleep(&sleep_time
, NULL
) < 0) {
439 if (kill(child_pid
, sig
) < 0) {
446 * Kill the child according to the two possible delays
448 static void watchdog(struct gengetopt_args_info
*ggo
)
453 if (!ggo
->timeout_term_given
&& !ggo
->timeout_kill_given
)
456 if (ggo
->daemonize_flag
) {
457 fputs("--timeout-XXXX is incompatible with --daemonize\n",
462 if (!ggo
->timeout_term_given
|| (ggo
->timeout_kill_given
&&
463 ggo
->timeout_kill_arg
<
464 ggo
->timeout_term_arg
)) {
465 /* SIGKILL before SIGTERM? Why not ;-) */
467 delay
= ggo
->timeout_kill_arg
;
470 delay
= ggo
->timeout_term_arg
;
473 wait_to_kill(delay
, sig
);
475 if (ggo
->timeout_term_given
!= ggo
->timeout_kill_given
)
478 delay
= ggo
->timeout_kill_arg
- ggo
->timeout_term_arg
;
482 /* The other signal */
483 sig
= SIGKILL
+ SIGTERM
- sig
;
484 wait_to_kill(delay
, sig
);
488 * The new process in its own namespace can now execute the command passed as
489 * argument or a shell
491 static int child_process(void *ggo_arg
)
493 struct gengetopt_args_info
*ggo
= ggo_arg
;
494 struct user_info user
;
499 if (setpriority(PRIO_PROCESS
, 0, ggo
->nice_arg
) < 0) {
500 perror("setpriority");
504 get_user_infos(&user
, ggo
);
508 if (ggo
->daemonize_flag
)
511 if (ggo
->alarm_given
)
512 alarm(ggo
->alarm_arg
);
514 if (ggo
->inputs_num
) {
515 char **argv
= xmalloc(sizeof(char *) * (ggo
->inputs_num
+ 1));
516 memcpy(argv
, ggo
->inputs
, ggo
->inputs_num
* sizeof(char *));
517 argv
[ggo
->inputs_num
] = NULL
;
518 execvp(argv
[0], argv
);
521 char *argv
[] = { NULL
, NULL
};
522 char *shell
= getenv("SHELL");
526 execv(argv
[0], argv
);
533 int main(int argc
, char *argv
[])
535 struct gengetopt_args_info args_info
;
540 ret
= cmdline_parser(argc
, argv
, &args_info
);
543 if (argc
- args_info
.inputs_num
<= 1) {
544 fputs("No flag specified, exiting\n", stderr
);
548 if (args_info
.alarm_given
&&
549 (args_info
.alarm_arg
< 0 || args_info
.alarm_arg
> 65535)) {
551 "The alarm value must be between 0 and 65535 got: %d\n",
552 args_info
.alarm_arg
);
556 if (args_info
.timeout_term_given
)
557 check_double_as_timespec(args_info
.timeout_term_arg
);
558 if (args_info
.timeout_kill_given
)
559 check_double_as_timespec(args_info
.timeout_kill_arg
);
561 flags
= get_flags(&args_info
);
562 fork_needed
= flags
!= 0 || args_info
.timeout_term_given
||
563 args_info
.timeout_kill_given
;
567 install_sigchld_handler(&args_info
);
568 install_signals_handlers();
570 child_pid
= clone(child_process
, &stack
[STACK_SIZE
- 1], flags
,
577 watchdog(&args_info
);
580 ret
= child_process(&args_info
);