Switched to autotools
[tlb/tomd.git] / src / tomd.c
CommitLineData
72af8b59
TB
1/* Copyright (C) 2018 Thomas Balzer */
2
3/* This file is part of tomd. */
4
5/* tomd is free software: you can redistribute it and/or modify */
6/* it under the terms of the GNU General Public License as published by */
7/* the Free Software Foundation, either version 3 of the License, or */
8/* (at your option) any later version. */
9
10/* tomd is distributed in the hope that it will be useful, */
11/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13/* GNU General Public License for more details. */
14
15/* You should have received a copy of the GNU General Public License */
16/* along with tomd. If not, see <http://www.gnu.org/licenses/>. */
17
229b92de 18#include <fcntl.h>
72af8b59
TB
19#include <stddef.h>
20#include <stdio.h>
21#include <errno.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <string.h>
25#include <sys/un.h>
26#include <sys/stat.h>
27#include <sys/socket.h>
7bd7fa83 28#include <sys/wait.h>
72af8b59 29
b6ca5637
TB
30#include "../include/macros.h"
31#include "../include/manifest.h"
32#include "../include/job.h"
57c82614 33
72af8b59
TB
34static void header(void)
35{
57c82614
TB
36 tomd_p("Tom's Daemon, Copyright (C) 2018 Thomas Balzer");
37 tomd_p("GPL v3 or later license.");
72af8b59
TB
38}
39
40static int sfd;
41static char socket_dirname[100];
42static char socket_filename[100];
43
44static void gen_socket_filename(void)
45{
46 uid_t this_user_id =
47 getuid();
48 sprintf(socket_dirname,
49 "/run/user/%d/tomd",
50 this_user_id);
51 sprintf(socket_filename,
52 "/run/user/%d/tomd/socket",
53 this_user_id);
54
57c82614 55 tomd_p("making dir '%s'", socket_dirname);
72af8b59 56 /* lazy way */
229b92de
TB
57 /* char buf[100]; */
58 /* sprintf(buf, "mkdir -p %s", socket_dirname); */
59 /* system(buf); */
60
61 if(mkdir(socket_dirname, S_IRWXU) != 0){
62 if(errno != EEXIST){
63 tomd_panic("socket mkdir");
64 }else{
65 tomd_p("socket dir exists.");
66 }
67 }
72af8b59 68
57c82614 69 tomd_p("removing file '%s'", socket_filename);
72af8b59
TB
70 if(unlink(socket_filename) != 0){
71 if(errno == ENOENT){
57c82614 72 tomd_p("socket file doesn't exist");
72af8b59 73 }else{
229b92de 74 tomd_panic("socket unlink");
72af8b59
TB
75 }
76 }
77}
78
57c82614 79static void socket_init(void)
72af8b59
TB
80{
81 gen_socket_filename();
1110e851 82
72af8b59
TB
83 /* listen on socket, respond to requests. */
84 /* perform all registered 'tick' operations */
85 sfd =
86 socket(PF_LOCAL, /* namespace - local unix socket */
87 SOCK_STREAM, /* style */
88 0); /* protocol */
89
90 if(sfd < 0){
229b92de
TB
91 tomd_panic("socket");
92 }
93
94 if(fcntl(sfd, F_SETFL, O_NONBLOCK) == -1){
95 tomd_panic("fnctl");
72af8b59
TB
96 }
97
98 struct sockaddr_un name;
99 size_t size;
100
101 name.sun_family = AF_LOCAL;
102 strncpy(name.sun_path,
103 socket_filename,
104 sizeof(name.sun_path));
105
106 size = SUN_LEN(&name);
57c82614 107 tomd_p("attempting to bind to '%s'",
72af8b59
TB
108 socket_filename);
109 if(bind(sfd,
110 (struct sockaddr *) &name,
111 size) < 0){
229b92de 112 tomd_panic("bind");
72af8b59
TB
113 }
114
57c82614
TB
115 tomd_p("initialized tomd socket connections");
116}
117
118static void init(void)
119{
120 socket_init();
121 load_jobs();
72af8b59
TB
122}
123
229b92de
TB
124#define SEND_ACK { \
125 int size; \
126 char ACK[4]; \
127 ACK[0] = 'A'; \
128 ACK[1] = 'C'; \
129 ACK[2] = 'K'; \
130 ACK[3] = '\0'; \
131 if(size = write(asfd, ACK, sizeof(ACK)) != sizeof(ACK)) { \
132 tomd_p("didn't send as much as we though (%d != %d)", \
133 size, sizeof(ACK)); \
134 } \
81215080
TB
135 }
136
72af8b59
TB
137#define CHDIR 0
138#define DONT_CHDIR 1
139#define CLOSE_PIPES 0
140#define DONT_CLOSE_PIPES 1
141
142static void daemonize(void)
143{
144 /* daemon is in unistd. */
145 /* arg1 - 0 changes dir to / */
146 /* arg2 - 0 closes all pipes (/dev/null) */
147 /* WARNING > THIS BEHAVIOR IS SILENT AND EASY TO MISPLACE THE
148 PROCESS */
229b92de 149 /* daemon will exit the original process. */
72af8b59
TB
150 daemon(DONT_CHDIR, DONT_CLOSE_PIPES);
151}
152
229b92de 153#define READ_SOCKET { \
1110e851 154 memset(buf, 0, sizeof(buf)); \
81215080
TB
155 int size = read(asfd, buf, 100); \
156 buf[size] = '\0'; \
157 }
158
159static int validate_sender(int asfd, char *buf)
160{
161 READ_SOCKET;
162 if(strcmp(buf, "client:tomc") == 0){
163 SEND_ACK;
164 return 0;
165 }
166 tomd_p("received '%s' instead of client:tomc", buf);
167 return -1;
168}
169
170enum requests { KILL, STATUS, STOP, START, UNKNOWN};
171#define X(a, b) { a, b }
172static struct {
173 char *str;
174 enum requests request;
175} request_types[] =
176 {
177 X("kill", KILL),
178 X("status", STATUS),
179 X("start", START),
180 X("stop", STOP),
181 X(NULL, -1)
182 };
183#undef X
184
185static void handle_request(int asfd, char *buf)
186{
187 READ_SOCKET;
188 enum requests request = UNKNOWN;
189 int i = 0;
190 while(1){
191 if(request_types[i].str == NULL){
192 break;
193 }
194
195 tomd_p("loop [%d]: comparing '%s' to '%s'",
196 i, buf, request_types[i].str);
197 if(strcmp(buf, request_types[i].str) == 0){
198 request = request_types[i].request;
199 break;
200 }
1110e851 201
81215080
TB
202 i++;
203 }
204
205 if(request == UNKNOWN){
206 tomd_p("unknown request type!");
207 return;
208 }
209
210 SEND_ACK;
211 READ_SOCKET;
212 // cross reference given name against known services
213 tomd_p("looking up '%s'", buf);
214 SEND_ACK;
7bd7fa83 215 struct job *jp = lookup_job(buf);
229b92de
TB
216#define DOUBLE_DUTY(...) { \
217 char tmp[100]; \
218 tomd_p(__VA_ARGS__); \
219 sprintf(tmp, __VA_ARGS__); \
220 int tmplen = strlen(tmp); \
221 tomd_p("__debug__:'%s'", tmp); \
222 write(asfd, tmp, tmplen); \
223 }
7bd7fa83
TB
224 if(jp == NULL){
225 DOUBLE_DUTY("lookup of '%s' failed.\n", buf);
226 } else {
7bd7fa83
TB
227 tomd_p("-----");
228 DOUBLE_DUTY("found job record for '%s'\n", jp->name);
229
230 if(request == STATUS){
231 if(jp->pid != -1){
232 int status = 0;
233 int n_pid = waitpid(jp->pid, &status, WNOHANG);
234 if(n_pid < 0){
235 perror("[tomd] waitpid");
236 }else if(n_pid == 0){
237 DOUBLE_DUTY("waitpid == 0, check manual\n");
1110e851 238 }else{
7bd7fa83
TB
239 DOUBLE_DUTY("running: %s\n",
240 !WIFEXITED(status) ? "yes"
241 : "no");
1110e851 242
7bd7fa83
TB
243 if(WIFEXITED(status)){
244 DOUBLE_DUTY(" status: %d\n", WEXITSTATUS(status));
245 jp->pid = -1;
246 jp->last_status = status;
247 }
248 }
249 }else{
250 DOUBLE_DUTY("running: no\n");
251 DOUBLE_DUTY(" status: %d\n", jp->last_status);
252 }
1110e851 253
7bd7fa83
TB
254 if(jp->pid != -1){
255 DOUBLE_DUTY(" pid: %d\n", jp->pid);
256 }
257 }else{
258 DOUBLE_DUTY("don't know how to handle that request.\n");
259 }
260 }
261 tomd_p("-----");
262 tomd_p("sending ack.");
263 SEND_ACK;
264 tomd_p("ack is sent.");
1110e851
TB
265 get_ack:
266 READ_SOCKET;
267 if(strcmp(buf, "ACK") != 0)
268 goto get_ack;
269 tomd_p("ack was ack'd.");
81215080
TB
270}
271
272static void handle_connection(int asfd)
273{
274 char buf[100] = {};
275
276 if(validate_sender(asfd, buf) != 0){
277 tomd_p("invalid sender.");
278 return;
279 }
280
281 tomd_p("validated client");
282 handle_request(asfd, buf);
283
7bd7fa83 284 shutdown(asfd, SHUT_WR);
1110e851 285 close(asfd);
81215080
TB
286}
287
72af8b59
TB
288static void run(void)
289{
290 /* loop: */
291 /* 1 listen */
292 /* 2 accept/select */
293 /* 3 handle dgram */
294 int listen_bool =
295 listen(sfd,
296 10); /* max connection limit is 10 for arbitrary reasons */
297 if(listen_bool == -1){
229b92de 298 tomd_panic("listen");
72af8b59
TB
299 }
300
301 for(;;){
da4c0e62 302 struct sockaddr addr;
81215080
TB
303 /* sometimes gives EINVAL if not initialized. */
304 socklen_t size = sizeof(addr);
81215080 305 errno = 0;
72af8b59
TB
306 int accept_sfd =
307 accept(sfd,
bd11173e 308 &addr, /* requester info */
da4c0e62 309 &size); /* len of requester info */
72af8b59 310 if(accept_sfd < 0){
81215080
TB
311 if(errno == EINVAL){
312 tomd_p("EINVAL");
229b92de
TB
313 }else if(errno == EWOULDBLOCK || errno == EAGAIN){
314 /* no worries */
315 }else {
316 tomd_panic("accept socket");
81215080 317 }
229b92de
TB
318 } else {
319 handle_connection(accept_sfd);
72af8b59 320 }
1110e851 321
229b92de
TB
322 check_root_job();
323 usleep(10);
72af8b59
TB
324 }
325}
326
327static void cleanup(void)
328{
329 if(sfd >= 0){
330 close(sfd);
331 }
332}
333
334int main(int argc, char **argv)
335{
336 header();
337 init();
7bd7fa83 338 /* daemonize(); */
229b92de
TB
339 run_jobs();
340 /* silent(); */
952aba97 341 run();
72af8b59 342 cleanup();
1110e851 343
72af8b59
TB
344 return EXIT_SUCCESS;
345}