Cmain.c -rw-r--r-- 4.4 KiB
1#define _POSIX_SOURCE
2#include <errno.h>
3#include <regex.h>
4#include <signal.h>
5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/socket.h>
10#include <sys/time.h>
11#include <sys/types.h>
12#include <sys/un.h>
13#include <sys/wait.h>
14#include <unistd.h>
15
16#define _MONITOR_REGEXP "unix:\\([-/.[:alnum:]]*\\)"
17#define _SHUTDOWN_CMD "system_powerdown\n"
18#define _GRACE_PERIOD 15
19#define _MAX_SOCKET_TIMEOUT 5
20
21struct _runtime {
22 char *socket_path;
23 int *pid;
24};
25
26struct _runtime _opts;
27
28static void _check_code_and_exit(int status) {
29 int result = WEXITSTATUS(status);
30 if (result == 0) {
31 puts("child process exited normally");
32 exit(EXIT_SUCCESS);
33 } else {
34 printf("child process exited with code: %d\n", result);
35 exit(EXIT_FAILURE);
36 }
37}
38
39static void _send_shutdown(char *path) {
40 int s, len;
41 struct sockaddr_un remote = {
42 .sun_family = AF_UNIX,
43 };
44 char str[100];
45 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
46 perror("socket");
47 exit(1);
48 }
49 struct timeval tv;
50 tv.tv_sec = _MAX_SOCKET_TIMEOUT;
51 tv.tv_usec = 0;
52 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv);
53 strcpy(remote.sun_path, _opts.socket_path);
54 len = strlen(remote.sun_path) + sizeof(remote.sun_family);
55 if (connect(s, (struct sockaddr *)&remote, len) == -1) {
56 perror("connect");
57 return;
58 }
59 printf("connected to %s\n", _opts.socket_path);
60 if (send(s, _SHUTDOWN_CMD, strlen(_SHUTDOWN_CMD) + 1, 0) == -1) {
61 perror("send");
62 return;
63 }
64
65 for (;;) {
66 len = recv(s, str, sizeof(str) - 1, 0);
67 switch (len) {
68 case -1:
69 perror("recv");
70 close(s);
71 return;
72 case 0:
73 close(s);
74 return;
75 }
76 }
77
78 close(s);
79}
80
81static void _shutdown() {
82 _send_shutdown(_opts.socket_path);
83 printf("process %d has %d seconds to shutdown\n", *_opts.pid, _GRACE_PERIOD);
84 int n_seconds = 0;
85 while (n_seconds < _GRACE_PERIOD) {
86 int stat;
87 if (waitpid(*_opts.pid, &stat, WNOHANG) == -1) {
88 printf("child process stopped, good bye!\n");
89 _check_code_and_exit(stat);
90 };
91 sleep(2);
92 n_seconds = n_seconds + 2;
93 }
94 printf("grace period ended, sending SIGTERM\n");
95 kill(*_opts.pid, SIGTERM);
96}
97
98static void signal_handler(int sig) {
99 printf("handling signal %d\n", sig);
100 switch (sig) {
101 case SIGTERM:
102 _shutdown();
103 case SIGINT:
104 _shutdown();
105 }
106}
107
108char *_get_arg_by_name(int argc, char *argv[], char *name) {
109 char *res;
110 int i;
111 for (i = 0; i < argc; i++) {
112 if (strcmp(argv[i], name) == 0 && (i < argc)) {
113 res = argv[i + 1];
114 return res;
115 }
116 }
117 return NULL;
118}
119
120char *_get_arg_with_regexp(int argc, char *argv[], char *name,
121 char *expression) {
122 char *arg_raw;
123 arg_raw = _get_arg_by_name(argc, argv, name);
124 if (arg_raw == NULL) {
125 return NULL;
126 }
127
128 regex_t regex;
129 regmatch_t groups[2];
130
131 if (regcomp(&regex, expression, 0) != 0) {
132 fprintf(stderr, "regexp failed to compile\n");
133 exit(1);
134 }
135
136 char *result = malloc(strlen(arg_raw) + 1);
137
138 if (regexec(&regex, arg_raw, 2, groups, 0) == 0) {
139 if (groups[1].rm_so == (size_t)-1) {
140 fprintf(stderr, "cannot match against flag:'%s'\n", arg_raw);
141 exit(1);
142 }
143 strcpy(result, arg_raw);
144 result[groups[1].rm_eo] = 0;
145 result = result + groups[1].rm_so;
146 } else {
147 fprintf(stderr, "cannot match against flag:'%s'\n", arg_raw);
148 exit(1);
149 }
150
151 regfree(&regex);
152 return result;
153}
154
155void _do_fork(char *argv[], int *pid) {
156 pid_t child_pid;
157 if ((child_pid = fork()) == -1) {
158 perror("fork");
159 exit(1);
160 }
161 if (child_pid == 0) {
162 setpgid(child_pid, child_pid);
163 execvp(argv[0], argv);
164 } else {
165 *pid = child_pid;
166 }
167}
168
169int main(int argc, char *argv[]) {
170 char *monitor_path = NULL;
171 monitor_path = _get_arg_with_regexp(argc, argv, "-monitor", _MONITOR_REGEXP);
172 if (monitor_path == NULL) {
173 printf("-monitor flag was not specified\n");
174 return EXIT_FAILURE;
175 }
176 _opts.socket_path = monitor_path;
177 char *_copy_args[argc];
178 _copy_args[argc - 1] = NULL;
179 for (int i = 1; i < argc; ++i) {
180 _copy_args[i - 1] = argv[i];
181 }
182
183 signal(SIGTERM, signal_handler);
184 signal(SIGINT, signal_handler);
185
186 int pid = 0;
187 int stat;
188
189 _do_fork(_copy_args, &pid);
190 _opts.pid = &pid;
191 printf("monitoring pid %d\n", pid);
192 while (waitpid(pid, &stat, 0) == -1) {
193 if (errno != EINTR) {
194 stat = -1;
195 break;
196 }
197 }
198 _check_code_and_exit(stat);
199}