static void test_rename_process_now(const char *p, int ret) { _cleanup_free_ char *comm = NULL, *cmdline = NULL; int r; r = rename_process(p); assert_se(r == ret || (ret == 0 && r >= 0) || (ret > 0 && r > 0)); if (r < 0) return; #if HAVE_VALGRIND_VALGRIND_H /* see above, valgrind is weird, we can't verify what we are doing here */ if (RUNNING_ON_VALGRIND) return; #endif assert_se(get_process_comm(0, &comm) >= 0); log_info("comm = <%s>", comm); assert_se(strneq(comm, p, 15)); assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0); /* we cannot expect cmdline to be renamed properly without privileges */ if (geteuid() == 0) { log_info("cmdline = <%s>", cmdline); assert_se(strneq(p, cmdline, STRLEN("test-process-util"))); assert_se(startswith(p, cmdline)); } else log_info("cmdline = <%s> (not verified)", cmdline); }
/* * This function is invoked in a forked process to run a server. * If the service is internal the appropriate function is invoked * otherwise the server program is exec'ed. * This function also logs the remote user id if appropriate */ void child_process( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; struct service_config *scp = SVC_CONF( sp ) ; const char *func = "child_process" ; signal_default_state(); if ((signals_pending[0] >= 0 && Sclose(signals_pending[0])) || (signals_pending[1] >= 0 && Sclose(signals_pending[1]))) { msg(LOG_ERR, func, "Failed to close the signal pipe: %m"); _exit(1); } signals_pending[0] = -1; signals_pending[1] = -1; Sclose(0); Sclose(1); Sclose(2); #ifdef DEBUG_SERVER if ( debug.on ) { msg( LOG_DEBUG, func, "Process %d is sleeping", getpid() ) ; sleep( 10 ) ; } #endif if ( ! SC_IS_INTERCEPTED( scp ) ) { set_credentials( scp ) ; if ( SC_SPECIFIED( scp, A_NICE ) ) (void) nice( SC_NICE( scp ) ) ; } if ( svc_child_access_control(sp, cp) != OK ) exit(0); if ( SERVER_LOGUSER( serp ) ) { unsigned timeout ; idresult_e result ; /* * We use LOGUSER_SUCCESS_TIMEOUT unless the service requires * identification, in which case we use an infinite timeout */ timeout = SC_MUST_IDENTIFY( scp ) ? 0 : LOGUSER_SUCCESS_TIMEOUT ; result = log_remote_user( serp, timeout ) ; if ( result != IDR_OK && SC_MUST_IDENTIFY( scp ) ) { svc_logprint( sp, NOID_ENTRY, "%s %s", conn_addrstr( SERVER_CONNECTION( serp ) ), idresult_explain( result ) ) ; _exit( 0 ) ; } } #ifdef HAVE_SESSIONCREATE if ( scp->sc_sessioncreate == YES ) { if ( SessionCreate(0, sessionHasTTY|sessionIsRemote) != noErr ) svc_logprint( sp, "SessionCreate", "SessionCreate() failed!" ); } #endif /* this is where the server gets executed -bbraun */ if ( ! SC_IS_INTERNAL( scp ) ) { if( scp->sc_redir_addr != NULL ) { redir_handler( serp ); } else { #if defined(HAVE_SETENV) char buff[1024]; strx_sprint(buff, sizeof(buff)-1, "REMOTE_HOST=%s", conn_addrstr(cp)); if( env_addstr(SC_ENV(scp)->env_handle, buff) != ENV_OK ) { msg( LOG_ERR, func, "Error adding REMOTE_HOST variable for %s: %m", SC_NAME(scp) ); _exit( 1 ) ; } #endif exec_server( serp ) ; } } else { char name[ 180 ] ; /* * We don't bother to disassociate from the controlling terminal * (we have a controlling terminal only if debug.on is TRUE) * * Also, for interceptor processes, we give them the name: * <program_name> <service-id> interceptor */ if ( SC_IS_INTERCEPTED( scp ) ) strx_print( INT_NULL, name, sizeof( name ) - 1, "%s %s interceptor", program_name, SC_ID( scp ) ) ; else { int namelen = sizeof( name ) - 1 ; /* leave space for the NUL */ char host[NI_MAXHOST]; size_t hostlen = NI_MAXHOST; socklen_t addrlen = 0; union xsockaddr *sinp = CONN_XADDRESS(SERVER_CONNECTION(serp)); int len; if( sinp == NULL ) exit(0); if( SC_IPV6(scp) ) addrlen = sizeof(struct sockaddr_in6); else if( SC_IPV4(scp) ) addrlen = sizeof(struct sockaddr_in); len = strx_nprint(name, namelen, "(%s service) %s", program_name, SC_ID( scp ) ) ; if( getnameinfo( SA(sinp), addrlen, host, hostlen, NULL, 0, 0) != 0 ) strcpy(host, "unknown"); if ( SC_IPV6(scp) && SC_ACCEPTS_CONNECTIONS( scp ) && !IN6_IS_ADDR_UNSPECIFIED(&sinp->sa_in6.sin6_addr) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s" , host ) ; if ( SC_IPV4(scp) && SC_ACCEPTS_CONNECTIONS( scp ) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s", host ) ; } rename_process( name ) ; SVC_INTERNAL( sp, serp ) ; } _exit( 0 ) ; /* NOTREACHED */ }
int stub_pid1(sd_id128_t uuid) { enum { STATE_RUNNING, STATE_REBOOT, STATE_POWEROFF, } state = STATE_RUNNING; sigset_t fullmask, oldmask, waitmask; usec_t quit_usec = USEC_INFINITY; pid_t pid; int r; /* The new environment we set up, on the stack. */ char new_environment[] = "container=systemd-nspawn\0" "container_uuid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful * for allowing arbitrary processes run in a container, and still have all zombies reaped. */ assert_se(sigfillset(&fullmask) >= 0); assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork child pid: %m"); if (pid == 0) { /* Return in the child */ assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0); setsid(); return 0; } reset_all_signal_handlers(); log_close(); close_all_fds(NULL, 0); log_open(); /* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also, * set $container= and $container_uuid= so that clients in the container that query it from /proc/1/environ * find them set set. */ sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX); reset_environ(new_environment, sizeof(new_environment)); rename_process("STUBINIT"); assert_se(sigemptyset(&waitmask) >= 0); assert_se(sigset_add_many(&waitmask, SIGCHLD, /* posix: process died */ SIGINT, /* sysv: ctrl-alt-del */ SIGRTMIN+3, /* systemd: halt */ SIGRTMIN+4, /* systemd: poweroff */ SIGRTMIN+5, /* systemd: reboot */ SIGRTMIN+6, /* systemd: kexec */ SIGRTMIN+13, /* systemd: halt */ SIGRTMIN+14, /* systemd: poweroff */ SIGRTMIN+15, /* systemd: reboot */ SIGRTMIN+16, /* systemd: kexec */ -1) >= 0); /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't * support reexec/reloading in this stub process. */ for (;;) { siginfo_t si; usec_t current_usec; si.si_pid = 0; r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); if (r < 0) { r = log_error_errno(errno, "Failed to reap children: %m"); goto finish; } current_usec = now(CLOCK_MONOTONIC); if (si.si_pid == pid || current_usec >= quit_usec) { /* The child we started ourselves died or we reached a timeout. */ if (state == STATE_REBOOT) { /* dispatch a queued reboot */ (void) reboot(RB_AUTOBOOT); r = log_error_errno(errno, "Failed to reboot: %m"); goto finish; } else if (state == STATE_POWEROFF) (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */ if (si.si_pid == pid && si.si_code == CLD_EXITED) r = si.si_status; /* pass on exit code */ else r = 255; /* signal, coredump, timeout, … */ goto finish; } if (si.si_pid != 0) /* We reaped something. Retry until there's nothing more to reap. */ continue; if (quit_usec == USEC_INFINITY) r = sigwaitinfo(&waitmask, &si); else { struct timespec ts; r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec)); } if (r < 0) { if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */ continue; if (errno == EAGAIN) /* timeout reached */ continue; r = log_error_errno(errno, "Failed to wait for signal: %m"); goto finish; } if (si.si_signo == SIGCHLD) continue; /* Let's reap this */ if (state != STATE_RUNNING) continue; /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a * constant… */ if (si.si_signo == SIGRTMIN+3 || si.si_signo == SIGRTMIN+4 || si.si_signo == SIGRTMIN+13 || si.si_signo == SIGRTMIN+14) state = STATE_POWEROFF; else if (si.si_signo == SIGINT || si.si_signo == SIGRTMIN+5 || si.si_signo == SIGRTMIN+6 || si.si_signo == SIGRTMIN+15 || si.si_signo == SIGRTMIN+16) state = STATE_REBOOT; else assert_not_reached("Got unexpected signal"); /* (void) kill_and_sigcont(pid, SIGTERM); */ quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; } finish: _exit(r < 0 ? EXIT_FAILURE : r); }