Hi. I have an autologin program which authenticates a user without asking for a password and starts a child process executing a user shell (for example, Bash, Xorg, or a Wayland compositor). This program is a systemd service. I discovered that systemd kills the autologin program, but does not kill the child of the autologin program. As I understand from the systemd documentation, systemd should kill both. I composed a minimal example in C at the end. It's lengthy. The service file `/etc/systemd/system/testa.service`: ``` [Unit] Description=Autologin After=systemd-user-sessions.service plymouth-quit-wait.service [Service] Type=simple ExecStart=autologin [Install] Alias=display-manager.service WantedBy=multi-user.target ``` I test it like this: ``` [root@beroal test-autologin]# ps --forest -e -o pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004 55809 55651 0::/user.slice/user-1002.sl 55808 11243 unconfined 0 \_ grep 1004 [root@beroal test-autologin]# systemctl start testa [root@beroal test-autologin]# ps --forest -e -o pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004 55824 55651 0::/user.slice/user-1002.sl 55823 11243 unconfined 0 \_ grep 1004 55812 1 0::/user.slice/user-1004.sl 55812 55812 unconfined 0 autologin 55822 55812 0::/user.slice/user-1004.sl 55812 55812 unconfined 0 \_ sleep 600 55814 1 0::/user.slice/user-1004.sl 55814 55814 unconfined 1004 /usr/lib/systemd/systemd --user 55815 55814 0::/user.slice/user-1004.sl 55814 55814 unconfined 1004 \_ (sd-pam) [root@beroal test-autologin]# systemctl stop testa [root@beroal test-autologin]# ps --forest -e -o pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004 55828 55651 0::/user.slice/user-1002.sl 55827 11243 unconfined 0 \_ grep 1004 55814 1 0::/user.slice/user-1004.sl 55814 55814 unconfined 1004 /usr/lib/systemd/systemd --user 55815 55814 0::/user.slice/user-1004.sl 55814 55814 unconfined 1004 \_ (sd-pam) 55822 1 0::/user.slice/user-1004.sl 55812 55812 unconfined 0 sleep 600 [root@beroal test-autologin]# systemctl kill --kill-who=all --signal=SIGTERM user-1004.slice [root@beroal test-autologin]# ps --forest -e -o pid,ppid,cgroup,pgid,sid,label,uid,command | grep 1004 55832 55651 0::/user.slice/user-1002.sl 55831 11243 unconfined 0 \_ grep 1004 ``` The child `sleep 600` process wasn't killed by stopping the service, but was killed by stopping the slice. Is this the intended behavior? Do I handle user authentication wrongly? Is there a way to kill a service fully? Arch Linux systemd version: 249.7-2 `autologin`: ```c #define _GNU_SOURCE #include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <grp.h> #include <pwd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/prctl.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <linux/kd.h> #include <linux/vt.h> #include <security/pam_appl.h> static int check_errno(char *operation, int error) { if (error != -1) return error; fprintf(stderr, "%s failed: %s (%d)\n", operation, strerror(errno), errno); exit(EXIT_FAILURE); } static int check_pam(struct pam_handle *handle, char *operation, int error) { if (error == PAM_SUCCESS) return 0; fprintf(stderr, "%s failed: %s\n", operation, pam_strerror(handle, error)); if (handle != NULL) pam_end(handle, error); exit(EXIT_FAILURE); } static int pam_null_conv(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) { (void)msg; (void)appdata_ptr; *resp = calloc(num_msg, sizeof (struct pam_response)); return *resp == NULL ? PAM_BUF_ERR : PAM_SUCCESS; } static int pam_putenv_tuple(struct pam_handle* handle, char* key, char* value) { char buf[256]; snprintf(buf, sizeof buf, "%s=%s", key, value); return pam_putenv(handle, buf); } static char *getenv_or(char *key, char *or) { char *var = getenv(key); return var ? var : or; } int main(void) { char *user_cmd[] = { "sleep", "600", NULL }; struct pam_conv conv = { pam_null_conv, NULL }; struct pam_handle* handle = NULL; check_pam(handle, "pam_start", pam_start("autologin", "test", &conv, &handle)); check_pam(handle, "pam_acct_mgmt", pam_acct_mgmt(handle, PAM_SILENT)); check_pam(handle, "pam_setcred", pam_setcred(handle, PAM_ESTABLISH_CRED)); struct passwd* pwd = getpwnam("test"); if (pwd == NULL) { fprintf(stderr, "getpwnam failed\n"); return 1; } check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "XDG_SEAT", "seat-guest")); check_pam(handle, "pam_putenv", pam_putenv(handle, "XDG_SESSION_CLASS=user")); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "HOME", pwd->pw_dir)); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "PWD", pwd->pw_dir)); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "SHELL", pwd->pw_shell)); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "USER", pwd->pw_name)); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "LOGNAME", pwd->pw_name)); check_pam(handle, "pam_putenv", pam_putenv_tuple(handle, "TERM", getenv_or("TERM", "linux"))); check_pam(handle, "pam_open_session", pam_open_session(handle, PAM_SILENT)); char** env = pam_getenvlist(handle); pid_t pid = check_errno("fork", fork()); if (pid == 0) execvpe(user_cmd[0], user_cmd, env); int res; while ((res = waitpid(pid, NULL, 0)) <= 0) { if (res == -1 && errno != EINTR) { fprintf(stderr, "waitpid failed: %s (%d)\n", strerror(errno), errno); break; } } check_pam(handle, "pam_close_session", pam_close_session(handle, PAM_SILENT)); check_pam(handle, "pam_setcred", pam_setcred(handle, PAM_DELETE_CRED)); check_pam(handle, "pam_end", pam_end(handle, 0)); return 0; } ```