class: center, middle name: start # Supervising processes </br> in modern Linux  .footer[15th October 2021</br>_Friday Talks_ @ ] --- class: left, middle # Danilo Spinella ### _Software Engineer in Packaging_ ### **SUSE**  --- class: middle, left ##  Contents What is a supervisor Initial setup Supervising the child using `pidfd` Listening on signals using `signalfd` Logging child `stdout` and `stderr` to a file --- class: left, middle #  What is a supervisor --- class: left, middle A supervisor is a software which only purpose is monitoring and controlling other processes --- class: middle, left ## Given a supervised process it can Check its status Restart it when it dies_*_ Log its output .footer[_*_ for long lived processes only] --- class: middle, left ##  It can be found in... Init (service manager) Containers Supervisors --- class: left, middle #  Initial setup --- class: left, middle ##  Goals Supervising a long lived process Restart it when it dies/exits Kill it when the supervisor exits Log its output --- class: left, middle ##Long lived process vs daemon Daemons double fork and become child of PID 1 --- class: left, middle ## Process to supervise Write the current time to stdout every 15 seconds --- class: left, middle ##  Writing `sleepy.c` --- class: left, middle ```c #include <stdio.h> #include <time.h> #include <unistd.h> int main() { while (1) { time_t rawtime; struct tm *timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); printf("[%d %d %d %d:%02d:%02d]\n", timeinfo->tm_mday, timeinfo->tm_mon + 1, timeinfo->tm_year + 1900, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); sleep(15); } } ``` --- class: left, middle ##  Writing `supervise.c` --- class: left, middle ## `fork(3p)` / `exec(3p)` --- class: left, middle # Supervising the child using `pidfd` --- class: left, middle ## `pidfd_open(2)` 1/2 ```c int pidfd_open(pid_t pid, unsigned int flags) ``` Create a file descriptor that refers to the process whose PID is specified in _pid_. --- class: left, middle ## `pidfd_open(2)` 2/2 ```c static int pidfd_open(pid_t pid, unsigned int flags) { return syscall(__NR_pidfd_open, pid, flags); } ``` ```c pid_t pid = fork(); if (pid != 0) { int pidfd = pidfd_open(pid, 0); } else { ... ``` --- class: left, middle ## `poll(3p)` Input/output multiplexing ```c int poll(struct pollfd fds[], nfds_t nfds, int timeout); ``` ```c struct pollfd pollfds; pollfds.fd = myfd; pollfds.events = POLLIN; poll(&pollfd, 1, -1); ``` --- class: left, middle ## `waitid(2)` ```c int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); ``` ```c static int waitid_pidfd(int pidfd) { siginfo_t siginfo; return syscall(SYS_waitid, P_PIDFD, pidfd, &siginfo, WEXITED); } ``` ```c waitid_pidfd(pidfd); ``` --- class: middle, left Opening a `/proc/[pid]` directory gives a `pidfd`... ...but it can't be waited using `waitid` --- class: left, middle # Listening on signals using `signalfd` --- class: left, middle The supervisor receive `SIGINT`  --- class: left, middle # Listening on signals Signal handler Waiting for signals in a thread Using signalfd --- class: left, middle ## `signalfd(2)` ```c int signalfd(int fd, const sigset_t *mask, int flags); ``` ***Note***: _The set of signals must be blocked in the current thread/process_. --- class: left, middle ## Kill the supervised process --- class: left, middle ## `pidfd_send_signal(2)` 1/2 ```c int pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags); ``` ```c static int pidfd_send_signal(int pidfd, int sig) { return syscall(SYS_pidfd_send_signal, pidfd, sig, NULL, 0); } ``` --- class: middle, left ##  Success The supervisor is working --- class: middle, left ##  signalfd Easier interface Can be handled in user-code Don't require a separate thread --- class: middle, left ##  signalfd Share the same problems as the other methods --- class: middle, left ##  pidfd No race condition Safely send a signal to a process --- class: middle, left ##  pidfd `send_signal` works only with processes in the same PID namespace or in one of its descendants `waidid` works only with children --- class: middle, left ## `SUBREAPER` 1/2 A subreaper fulfills the role of init(1) for its descendant processes --- class: middle, left ## `SUBREAPER` 2/2 The subreaper will receive a `SIGCHLD` signal and is able to `wait(2)` on the process --- class: middle, left # Logging child </br>`stdout` and `stderr`</br>to a file --- class: middle, left `stdin` and `stdout` are `fd` --- background-image: url(/img/supervising_processes_in_modern_linux/pipe.svg) --- background-image: url(/img/supervising_processes_in_modern_linux/pipe2.svg) --- class: middle, left Separate thread to `poll` both `stdout` and `stdin` --- class: left, middle ##  Success --- class: center, middle # Questions  --- class: center, middle #   ## This work is licensed under ## _Attribution-ShareAlike 4.0 International</br> (CC BY-SA 4.0)_ .footer[Icons from _www.onlinewebfonts.com_, which are licensed under CC BY 3.0] --- template: start