Subversion Repositories Mokou

Rev

Rev 13 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 nishi 1
/* $Id: service.c 14 2024-09-07 13:07:50Z nishi $ */
2
 
3
#include "mk_service.h"
4
 
5 nishi 5
#include <fcntl.h>
3 nishi 6
#include <stdio.h>
2 nishi 7
#include <dirent.h>
8
#include <stdlib.h>
3 nishi 9
#include <string.h>
10
#include <sys/stat.h>
5 nishi 11
#include <signal.h>
12
#include <unistd.h>
13
#include <sys/wait.h>
2 nishi 14
 
15
#include "mk_log.h"
16
#include "mk_util.h"
17
 
18
struct mk_service** services = NULL;
19
 
6 nishi 20
#ifdef __linux__
21
const char* sys_signame[] = {
8 nishi 22
	"",
6 nishi 23
	"HUP",
24
	"INT",
25
	"QUIT",
26
	"ILL",
27
	"TRAP",
28
	"ABRT",
29
	"BUS",
30
	"FPE",
31
	"KILL",
32
	"USR1",
33
	"SEGV",
34
	"USR2",
35
	"PIPE",
36
	"ALRM",
37
	"TERM",
38
	"STKFLT",
39
	"CHLD",
40
	"CONT",
41
	"STOP",
42
	"TSTP",
43
	"TTIN",
44
	"TTOU",
45
	"URG",
46
	"XCPU",
47
	"XFSZ",
48
	"VTALRM",
49
	"PROF",
50
	"WINCH",
51
	"POLL",
52
	"PWR",
53
	"SYS",
54
	"RTMIN"
55
};
56
#endif
57
 
2 nishi 58
void mk_service_scan(void){
59
	if(services != NULL){
60
		int i;
61
		for(i = 0; services[i] != NULL; i++){
5 nishi 62
			if(services[i]->name != NULL) free(services[i]->name);
63
			if(services[i]->stop != NULL) free(services[i]->stop);
4 nishi 64
			if(services[i]->description != NULL) free(services[i]->description);
65
			if(services[i]->exec != NULL) free(services[i]->exec);
66
			if(services[i]->pidfile != NULL) free(services[i]->pidfile);
2 nishi 67
			free(services[i]);
68
		}
69
		free(services);
3 nishi 70
		mk_log("Cleaning up the list");
2 nishi 71
	}
3 nishi 72
	services = malloc(sizeof(*services));
73
	services[0] = NULL;
2 nishi 74
 
75
	mk_log("Scanning the service directory.");
76
 
77
	DIR* dir = opendir(PREFIX "/etc/mokou");
78
	if(dir != NULL){
79
		struct dirent* d;
80
		while((d = readdir(dir)) != NULL){
81
			if(mk_endswith(d->d_name, ".conf")){
82
				char* path = mk_strcat(PREFIX "/etc/mokou/", d->d_name);
83
				char* str = mk_strcat("Reading ", path);
84
				mk_log(str);
85
				free(str);
3 nishi 86
 
87
				FILE* f = fopen(path, "r");
88
				if(f != NULL){
89
					struct stat s;
90
					stat(path, &s);
91
					char* buffer = malloc(s.st_size + 1);
92
					buffer[s.st_size] = 0;
93
					fread(buffer, s.st_size, 1, f);
94
					int i;
95
					int incr = 0;
96
 
97
					char* desc = NULL;
98
					char* exec = NULL;
5 nishi 99
					char* stop = NULL;
3 nishi 100
					char* pidfile = NULL;
13 nishi 101
					uid_t uid = 0;
102
					gid_t gid = 0;
103
					bool bad = false;
3 nishi 104
 
105
					for(i = 0;; i++){
106
						if(buffer[i] == '\n' || buffer[i] == 0){
107
							char oldc = buffer[i];
108
							buffer[i] = 0;
109
 
110
							char* line = buffer + incr;
111
 
112
							if(strlen(line) > 0 && line[0] != '#'){
113
								int j;
114
 
115
								for(j = 0; line[j] != 0; j++){
116
									if(line[j] == '='){
117
										line[j] = 0;
118
 
119
										char* key = line;
120
										char* value = line + j + 1;
121
										if(strcmp(key, "description") == 0){
122
											if(desc != NULL) free(desc);
123
											desc = mk_strdup(value);
124
										}else if(strcmp(key, "exec") == 0){
125
											if(exec != NULL) free(exec);
126
											exec = mk_strdup(value);
127
										}else if(strcmp(key, "pidfile") == 0){
128
											if(pidfile != NULL) free(pidfile);
129
											pidfile = mk_strdup(value);
5 nishi 130
										}else if(strcmp(key, "stop") == 0){
131
											if(stop != NULL) free(stop);
132
											stop = mk_strdup(value);
13 nishi 133
										}else if(strcmp(key, "user") == 0){
134
											struct passwd* p = getpwnam(value);
135
											if(p != NULL){
136
												uid = p->pw_uid;
137
												gid = p->pw_gid;
138
											}else{
139
												mk_log("Could not find the specified user");
140
												bad = true;
141
											}
3 nishi 142
										}
143
 
144
										break;
145
									}
146
								}
147
							}
148
 
149
							incr = i + 1;
150
							if(oldc == 0) break;
151
						}
152
					}
153
					fclose(f);
154
 
155
					if(exec == NULL){
156
						char* log = mk_strcat(desc == NULL ? path : desc, ": Missing exec");
157
						mk_log(log);
158
						free(log);
159
						bad = true;
160
					}
161
					if(pidfile == NULL){
162
						char* log = mk_strcat(desc == NULL ? path : desc, ": Missing pidfile");
163
						mk_log(log);
164
						free(log);
165
						bad = true;
166
					}
167
 
168
					if(!bad){
169
						char* log = mk_strcat3("Adding ", desc == NULL ? path : desc, " to the list");
170
						mk_log(log);
171
						free(log);
4 nishi 172
 
5 nishi 173
						int i;
4 nishi 174
						struct mk_service* serv = malloc(sizeof(*serv));
5 nishi 175
						serv->name = mk_strdup(d->d_name);
176
 
177
						for(i = strlen(d->d_name) - 1; i >= 0; i--){
178
							if(serv->name[i] == '.'){
179
								serv->name[i] = 0;
180
								break;
181
							}
182
						}
183
 
4 nishi 184
						serv->description = desc != NULL ? mk_strdup(desc) : NULL;
5 nishi 185
						serv->stop = stop != NULL ? mk_strdup(stop) : NULL;
4 nishi 186
						serv->exec = mk_strdup(exec);
187
						serv->pidfile = mk_strdup(pidfile);
13 nishi 188
						serv->uid = uid;
189
						serv->gid = gid;
5 nishi 190
						serv->stopped = false;
191
 
192
						struct mk_service** oldsrvs = services;
193
						for(i = 0; oldsrvs[i] != NULL; i++);
194
						services = malloc(sizeof(*services) * (i + 2));
195
						for(i = 0; oldsrvs[i] != NULL; i++){
196
							services[i] = oldsrvs[i];
197
						}
198
						services[i] = serv;
199
						services[i + 1] = NULL;
200
						free(oldsrvs);
3 nishi 201
					}
202
 
203
					if(desc != NULL) free(desc);
204
					if(exec != NULL) free(exec);
205
					if(pidfile != NULL) free(pidfile);
206
				}
207
 
2 nishi 208
				free(path);
209
			}
210
		}
211
		closedir(dir);
212
	}else{
213
		mk_log("Cannot open the directory.");
214
	}
215
}
5 nishi 216
 
8 nishi 217
const char* mk_errors[] = {
5 nishi 218
	"Success",
219
	"No such service",
220
	"Service is alive",
221
	"Failed to start",
222
	"Service is dead",
223
	"Bad signal",
12 nishi 224
	"Could not stop the service",
225
	"Could not run the stop command"
5 nishi 226
};
227
 
228
int mk_stop_service(const char* name){
229
	int i;
230
	for(i = 0; services[i] != NULL; i++){
231
		if(strcmp(services[i]->name, name) == 0){
232
			struct mk_service* srv = services[i];
233
			char* log = mk_strcat("Stopping ", name);
234
			mk_log(log);
235
			free(log);
236
 
14 nishi 237
			srv->stopped = true; /* No more resurrecting */
238
 
5 nishi 239
			bool alive = false;
240
 
241
			FILE* f = fopen(srv->pidfile, "r");
242
			unsigned long long pid;
243
			if(f != NULL){
244
				fscanf(f, "%llu", &pid);
245
				fclose(f);
246
				alive = kill(pid, 0) == 0;
247
			}
248
 
249
			if(!alive){
250
				mk_log("Process seems to be dead, not stopping");
251
				return 4;
252
			}
253
 
254
			if(srv->stop == NULL || srv->stop[0] == '#'){
255
				int sig = -1;
256
				if(srv->stop == NULL){
257
					sig = SIGINT;
258
				}
259
				if(sig == -1){
260
					int i;
261
					for(i = 1; i < NSIG; i++){
262
						if(strcmp(sys_signame[i], srv->stop + 1) == 0){
263
							sig = i;
264
							break;
265
						}
266
					}
267
				}
268
				if(sig == -1){
269
					mk_log("Bad signal");
270
					return 5;
271
				}else{
272
					log = mk_strcat("Sending SIG", sys_signame[sig]);
273
					mk_log(log);
274
					free(log);
275
					kill(pid, sig);
276
				}
277
			}else{
278
				char** pargv = malloc(sizeof(*pargv));
279
				pargv[0] = NULL;
280
 
281
				int i;
282
				int incr = 0;
283
				for(i = 0;; i++){
284
					if(srv->stop[i] == 0 || srv->stop[i] == ' '){
285
						char* str = malloc(i - incr + 1);
286
						memcpy(str, srv->stop + incr, i - incr);
287
						str[i - incr] = 0;
288
 
289
						char** oldargv = pargv;
290
						int j;
291
						for(j = 0; oldargv[j] != NULL; j++);
292
						pargv = malloc(sizeof(*pargv) * (j + 2));
293
						for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
294
						pargv[j] = str;
295
						pargv[j + 1]  = NULL;
296
						free(oldargv);
297
 
298
						incr = i + 1;
299
						if(srv->exec[i] == 0) break;
300
					}
301
				}
12 nishi 302
 
303
				bool fail = false;
304
				pid_t pid = fork();
305
				if(pid == 0){
306
					int n = open("/dev/null", O_RDWR);
307
					dup2(n, 1);
308
					dup2(n, 2);
13 nishi 309
					setgid(srv->gid);
310
					setegid(srv->gid);
311
					setuid(srv->uid);
312
					seteuid(srv->uid);
12 nishi 313
					execvp(pargv[0], pargv);
314
					_exit(-1);
315
				}else{
316
					int status;
317
					waitpid(pid, &status, 0);
318
					if(WEXITSTATUS(status) != 0) fail = true;
319
				}
320
 
321
				for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
322
				free(pargv);
323
 
324
				if(fail){
325
					mk_log("Failed to run stop command");
326
					return 7;
327
				}
5 nishi 328
			}
12 nishi 329
 
330
			usleep(100);
5 nishi 331
 
12 nishi 332
			bool dead = false;
333
			for(i = 0; i < 3; i++){
334
				if(kill(pid, 0) == -1){
335
					mk_log("Process died");
336
					dead = true;
337
					break;
338
				}else{
339
					mk_log("Process is still alive");
340
				}
341
				if(i != 2) sleep(1);
342
			}
343
			if(!dead){
344
				mk_log("Could not kill the process");
345
				return 6;
346
			}
347
 
5 nishi 348
			return 0;
349
		}
350
	}
351
	return 1;
352
}
353
 
354
int mk_start_service(const char* name){
355
	int i;
356
	for(i = 0; services[i] != NULL; i++){
357
		if(strcmp(services[i]->name, name) == 0){
358
			struct mk_service* srv = services[i];
359
			char* log = mk_strcat("Starting ", name);
360
			mk_log(log);
361
			free(log);
362
 
363
			bool alive = false;
364
 
365
			FILE* f = fopen(srv->pidfile, "r");
366
			if(f != NULL){
367
				unsigned long long pid;
368
				fscanf(f, "%llu", &pid);
369
				fclose(f);
370
				alive = kill(pid, 0) == 0;
371
			}
372
			if(alive){
373
				mk_log("Process seems to be alive, not starting");
374
				return 2;
375
			}
376
 
377
			char** pargv = malloc(sizeof(*pargv));
378
			pargv[0] = NULL;
379
 
380
			int i;
381
			int incr = 0;
382
			for(i = 0;; i++){
383
				if(srv->exec[i] == 0 || srv->exec[i] == ' '){
384
					char* str = malloc(i - incr + 1);
385
					memcpy(str, srv->exec + incr, i - incr);
386
					str[i - incr] = 0;
387
 
388
					char** oldargv = pargv;
389
					int j;
390
					for(j = 0; oldargv[j] != NULL; j++);
391
					pargv = malloc(sizeof(*pargv) * (j + 2));
392
					for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
393
					pargv[j] = str;
394
					pargv[j + 1]  = NULL;
395
					free(oldargv);
396
 
397
					incr = i + 1;
398
					if(srv->exec[i] == 0) break;
399
				}
400
			}
401
 
402
			bool fail = false;
403
 
404
			pid_t pid = fork();
405
			if(pid == 0){
406
				int n = open("/dev/null", O_RDWR);
407
				dup2(n, 1);
408
				dup2(n, 2);
13 nishi 409
				setgid(srv->gid);
410
				setegid(srv->gid);
411
				setuid(srv->uid);
412
				seteuid(srv->uid);
5 nishi 413
				execvp(pargv[0], pargv);
414
				_exit(-1);
415
			}else{
416
				int status;
417
				waitpid(pid, &status, 0);
418
				if(WEXITSTATUS(status) != 0) fail = true;
419
			}
420
			for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
421
			free(pargv);
422
			if(fail){
423
				log = mk_strcat("Failed to start ", name);
424
				mk_log(log);
425
				free(log);
426
				srv->stopped = false;
427
				return 3;
428
			}else{
429
				log = mk_strcat("Started ", name);
430
				mk_log(log);
431
				free(log);
432
				srv->stopped = false;
433
			}
434
 
435
			return 0;
436
		}
437
	}
438
	return 1;
439
}
440
 
441
void mk_start_services(void){
442
	int i;
443
	for(i = 0; services[i] != NULL; i++){
444
		mk_start_service(services[i]->name);
445
	}
446
}
10 nishi 447
 
448
void mk_resurrect_services(void){
449
	int i;
450
	bool re = false;
451
	for(i = 0; services[i] != NULL; i++){
452
		if(!services[i]->stopped){
453
			bool alive = false;
454
 
455
			FILE* f = fopen(services[i]->pidfile, "r");
456
			if(f != NULL){
457
				unsigned long long pid;
458
				fscanf(f, "%llu", &pid);
459
				fclose(f);
460
				alive = kill(pid, 0) == 0;
461
			}
462
			if(!alive){
463
				if(!re){
464
					mk_log("Resurrection");
465
					re = true;
466
				}
467
				mk_start_service(services[i]->name);
468
			}
469
		}
470
	}
471
}