Subversion Repositories Mokou

Rev

Rev 12 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 nishi 1
/* $Id: service.c 13 2024-09-07 12:44:18Z 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
 
237
			bool alive = false;
238
 
239
			FILE* f = fopen(srv->pidfile, "r");
240
			unsigned long long pid;
241
			if(f != NULL){
242
				fscanf(f, "%llu", &pid);
243
				fclose(f);
244
				alive = kill(pid, 0) == 0;
245
			}
246
 
247
			if(!alive){
248
				mk_log("Process seems to be dead, not stopping");
249
				return 4;
250
			}
251
 
252
			if(srv->stop == NULL || srv->stop[0] == '#'){
253
				int sig = -1;
254
				if(srv->stop == NULL){
255
					sig = SIGINT;
256
				}
257
				if(sig == -1){
258
					int i;
259
					for(i = 1; i < NSIG; i++){
260
						if(strcmp(sys_signame[i], srv->stop + 1) == 0){
261
							sig = i;
262
							break;
263
						}
264
					}
265
				}
266
				if(sig == -1){
267
					mk_log("Bad signal");
268
					return 5;
269
				}else{
270
					log = mk_strcat("Sending SIG", sys_signame[sig]);
271
					mk_log(log);
272
					free(log);
273
					kill(pid, sig);
274
				}
275
			}else{
276
				char** pargv = malloc(sizeof(*pargv));
277
				pargv[0] = NULL;
278
 
279
				int i;
280
				int incr = 0;
281
				for(i = 0;; i++){
282
					if(srv->stop[i] == 0 || srv->stop[i] == ' '){
283
						char* str = malloc(i - incr + 1);
284
						memcpy(str, srv->stop + incr, i - incr);
285
						str[i - incr] = 0;
286
 
287
						char** oldargv = pargv;
288
						int j;
289
						for(j = 0; oldargv[j] != NULL; j++);
290
						pargv = malloc(sizeof(*pargv) * (j + 2));
291
						for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
292
						pargv[j] = str;
293
						pargv[j + 1]  = NULL;
294
						free(oldargv);
295
 
296
						incr = i + 1;
297
						if(srv->exec[i] == 0) break;
298
					}
299
				}
12 nishi 300
 
301
				bool fail = false;
302
				pid_t pid = fork();
303
				if(pid == 0){
304
					int n = open("/dev/null", O_RDWR);
305
					dup2(n, 1);
306
					dup2(n, 2);
13 nishi 307
					setgid(srv->gid);
308
					setegid(srv->gid);
309
					setuid(srv->uid);
310
					seteuid(srv->uid);
12 nishi 311
					execvp(pargv[0], pargv);
312
					_exit(-1);
313
				}else{
314
					int status;
315
					waitpid(pid, &status, 0);
316
					if(WEXITSTATUS(status) != 0) fail = true;
317
				}
318
 
319
				for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
320
				free(pargv);
321
 
322
				if(fail){
323
					mk_log("Failed to run stop command");
324
					return 7;
325
				}
5 nishi 326
			}
12 nishi 327
 
328
			usleep(100);
5 nishi 329
 
12 nishi 330
			bool dead = false;
331
			for(i = 0; i < 3; i++){
332
				if(kill(pid, 0) == -1){
333
					mk_log("Process died");
334
					dead = true;
335
					break;
336
				}else{
337
					mk_log("Process is still alive");
338
				}
339
				if(i != 2) sleep(1);
340
			}
341
			if(!dead){
342
				mk_log("Could not kill the process");
343
				return 6;
344
			}
345
 
5 nishi 346
			srv->stopped = true;
347
			return 0;
348
		}
349
	}
350
	return 1;
351
}
352
 
353
int mk_start_service(const char* name){
354
	int i;
355
	for(i = 0; services[i] != NULL; i++){
356
		if(strcmp(services[i]->name, name) == 0){
357
			struct mk_service* srv = services[i];
358
			char* log = mk_strcat("Starting ", name);
359
			mk_log(log);
360
			free(log);
361
 
362
			bool alive = false;
363
 
364
			FILE* f = fopen(srv->pidfile, "r");
365
			if(f != NULL){
366
				unsigned long long pid;
367
				fscanf(f, "%llu", &pid);
368
				fclose(f);
369
				alive = kill(pid, 0) == 0;
370
			}
371
			if(alive){
372
				mk_log("Process seems to be alive, not starting");
373
				return 2;
374
			}
375
 
376
			char** pargv = malloc(sizeof(*pargv));
377
			pargv[0] = NULL;
378
 
379
			int i;
380
			int incr = 0;
381
			for(i = 0;; i++){
382
				if(srv->exec[i] == 0 || srv->exec[i] == ' '){
383
					char* str = malloc(i - incr + 1);
384
					memcpy(str, srv->exec + incr, i - incr);
385
					str[i - incr] = 0;
386
 
387
					char** oldargv = pargv;
388
					int j;
389
					for(j = 0; oldargv[j] != NULL; j++);
390
					pargv = malloc(sizeof(*pargv) * (j + 2));
391
					for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
392
					pargv[j] = str;
393
					pargv[j + 1]  = NULL;
394
					free(oldargv);
395
 
396
					incr = i + 1;
397
					if(srv->exec[i] == 0) break;
398
				}
399
			}
400
 
401
			bool fail = false;
402
 
403
			pid_t pid = fork();
404
			if(pid == 0){
405
				int n = open("/dev/null", O_RDWR);
406
				dup2(n, 1);
407
				dup2(n, 2);
13 nishi 408
				setgid(srv->gid);
409
				setegid(srv->gid);
410
				setuid(srv->uid);
411
				seteuid(srv->uid);
5 nishi 412
				execvp(pargv[0], pargv);
413
				_exit(-1);
414
			}else{
415
				int status;
416
				waitpid(pid, &status, 0);
417
				if(WEXITSTATUS(status) != 0) fail = true;
418
			}
419
			for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
420
			free(pargv);
421
			if(fail){
422
				log = mk_strcat("Failed to start ", name);
423
				mk_log(log);
424
				free(log);
425
				srv->stopped = false;
426
				return 3;
427
			}else{
428
				log = mk_strcat("Started ", name);
429
				mk_log(log);
430
				free(log);
431
				srv->stopped = false;
432
			}
433
 
434
			return 0;
435
		}
436
	}
437
	return 1;
438
}
439
 
440
void mk_start_services(void){
441
	int i;
442
	for(i = 0; services[i] != NULL; i++){
443
		mk_start_service(services[i]->name);
444
	}
445
}
10 nishi 446
 
447
void mk_resurrect_services(void){
448
	int i;
449
	bool re = false;
450
	for(i = 0; services[i] != NULL; i++){
451
		if(!services[i]->stopped){
452
			bool alive = false;
453
 
454
			FILE* f = fopen(services[i]->pidfile, "r");
455
			if(f != NULL){
456
				unsigned long long pid;
457
				fscanf(f, "%llu", &pid);
458
				fclose(f);
459
				alive = kill(pid, 0) == 0;
460
			}
461
			if(!alive){
462
				if(!re){
463
					mk_log("Resurrection");
464
					re = true;
465
				}
466
				mk_start_service(services[i]->name);
467
			}
468
		}
469
	}
470
}