Subversion Repositories Mokou

Rev

Rev 10 | Rev 13 | 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 12 2024-09-07 10:18:01Z 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;
101
 
102
					for(i = 0;; i++){
103
						if(buffer[i] == '\n' || buffer[i] == 0){
104
							char oldc = buffer[i];
105
							buffer[i] = 0;
106
 
107
							char* line = buffer + incr;
108
 
109
							if(strlen(line) > 0 && line[0] != '#'){
110
								int j;
111
 
112
								for(j = 0; line[j] != 0; j++){
113
									if(line[j] == '='){
114
										line[j] = 0;
115
 
116
										char* key = line;
117
										char* value = line + j + 1;
118
										if(strcmp(key, "description") == 0){
119
											if(desc != NULL) free(desc);
120
											desc = mk_strdup(value);
121
										}else if(strcmp(key, "exec") == 0){
122
											if(exec != NULL) free(exec);
123
											exec = mk_strdup(value);
124
										}else if(strcmp(key, "pidfile") == 0){
125
											if(pidfile != NULL) free(pidfile);
126
											pidfile = mk_strdup(value);
5 nishi 127
										}else if(strcmp(key, "stop") == 0){
128
											if(stop != NULL) free(stop);
129
											stop = mk_strdup(value);
3 nishi 130
										}
131
 
132
										break;
133
									}
134
								}
135
							}
136
 
137
							incr = i + 1;
138
							if(oldc == 0) break;
139
						}
140
					}
141
					fclose(f);
142
 
143
					bool bad = false;
144
					if(exec == NULL){
145
						char* log = mk_strcat(desc == NULL ? path : desc, ": Missing exec");
146
						mk_log(log);
147
						free(log);
148
						bad = true;
149
					}
150
					if(pidfile == NULL){
151
						char* log = mk_strcat(desc == NULL ? path : desc, ": Missing pidfile");
152
						mk_log(log);
153
						free(log);
154
						bad = true;
155
					}
156
 
157
					if(!bad){
158
						char* log = mk_strcat3("Adding ", desc == NULL ? path : desc, " to the list");
159
						mk_log(log);
160
						free(log);
4 nishi 161
 
5 nishi 162
						int i;
4 nishi 163
						struct mk_service* serv = malloc(sizeof(*serv));
5 nishi 164
						serv->name = mk_strdup(d->d_name);
165
 
166
						for(i = strlen(d->d_name) - 1; i >= 0; i--){
167
							if(serv->name[i] == '.'){
168
								serv->name[i] = 0;
169
								break;
170
							}
171
						}
172
 
4 nishi 173
						serv->description = desc != NULL ? mk_strdup(desc) : NULL;
5 nishi 174
						serv->stop = stop != NULL ? mk_strdup(stop) : NULL;
4 nishi 175
						serv->exec = mk_strdup(exec);
176
						serv->pidfile = mk_strdup(pidfile);
5 nishi 177
						serv->stopped = false;
178
 
179
						struct mk_service** oldsrvs = services;
180
						for(i = 0; oldsrvs[i] != NULL; i++);
181
						services = malloc(sizeof(*services) * (i + 2));
182
						for(i = 0; oldsrvs[i] != NULL; i++){
183
							services[i] = oldsrvs[i];
184
						}
185
						services[i] = serv;
186
						services[i + 1] = NULL;
187
						free(oldsrvs);
3 nishi 188
					}
189
 
190
					if(desc != NULL) free(desc);
191
					if(exec != NULL) free(exec);
192
					if(pidfile != NULL) free(pidfile);
193
				}
194
 
2 nishi 195
				free(path);
196
			}
197
		}
198
		closedir(dir);
199
	}else{
200
		mk_log("Cannot open the directory.");
201
	}
202
}
5 nishi 203
 
8 nishi 204
const char* mk_errors[] = {
5 nishi 205
	"Success",
206
	"No such service",
207
	"Service is alive",
208
	"Failed to start",
209
	"Service is dead",
210
	"Bad signal",
12 nishi 211
	"Could not stop the service",
212
	"Could not run the stop command"
5 nishi 213
};
214
 
215
int mk_stop_service(const char* name){
216
	int i;
217
	for(i = 0; services[i] != NULL; i++){
218
		if(strcmp(services[i]->name, name) == 0){
219
			struct mk_service* srv = services[i];
220
			char* log = mk_strcat("Stopping ", name);
221
			mk_log(log);
222
			free(log);
223
 
224
			bool alive = false;
225
 
226
			FILE* f = fopen(srv->pidfile, "r");
227
			unsigned long long pid;
228
			if(f != NULL){
229
				fscanf(f, "%llu", &pid);
230
				fclose(f);
231
				alive = kill(pid, 0) == 0;
232
			}
233
 
234
			if(!alive){
235
				mk_log("Process seems to be dead, not stopping");
236
				return 4;
237
			}
238
 
239
			if(srv->stop == NULL || srv->stop[0] == '#'){
240
				int sig = -1;
241
				if(srv->stop == NULL){
242
					sig = SIGINT;
243
				}
244
				if(sig == -1){
245
					int i;
246
					for(i = 1; i < NSIG; i++){
247
						if(strcmp(sys_signame[i], srv->stop + 1) == 0){
248
							sig = i;
249
							break;
250
						}
251
					}
252
				}
253
				if(sig == -1){
254
					mk_log("Bad signal");
255
					return 5;
256
				}else{
257
					log = mk_strcat("Sending SIG", sys_signame[sig]);
258
					mk_log(log);
259
					free(log);
260
					kill(pid, sig);
261
				}
262
			}else{
263
				char** pargv = malloc(sizeof(*pargv));
264
				pargv[0] = NULL;
265
 
266
				int i;
267
				int incr = 0;
268
				for(i = 0;; i++){
269
					if(srv->stop[i] == 0 || srv->stop[i] == ' '){
270
						char* str = malloc(i - incr + 1);
271
						memcpy(str, srv->stop + incr, i - incr);
272
						str[i - incr] = 0;
273
 
274
						char** oldargv = pargv;
275
						int j;
276
						for(j = 0; oldargv[j] != NULL; j++);
277
						pargv = malloc(sizeof(*pargv) * (j + 2));
278
						for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
279
						pargv[j] = str;
280
						pargv[j + 1]  = NULL;
281
						free(oldargv);
282
 
283
						incr = i + 1;
284
						if(srv->exec[i] == 0) break;
285
					}
286
				}
12 nishi 287
 
288
				bool fail = false;
289
				pid_t pid = fork();
290
				if(pid == 0){
291
					int n = open("/dev/null", O_RDWR);
292
					dup2(n, 1);
293
					dup2(n, 2);
294
					execvp(pargv[0], pargv);
295
					_exit(-1);
296
				}else{
297
					int status;
298
					waitpid(pid, &status, 0);
299
					if(WEXITSTATUS(status) != 0) fail = true;
300
				}
301
 
302
				for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
303
				free(pargv);
304
 
305
				if(fail){
306
					mk_log("Failed to run stop command");
307
					return 7;
308
				}
5 nishi 309
			}
12 nishi 310
 
311
			usleep(100);
5 nishi 312
 
12 nishi 313
			bool dead = false;
314
			for(i = 0; i < 3; i++){
315
				if(kill(pid, 0) == -1){
316
					mk_log("Process died");
317
					dead = true;
318
					break;
319
				}else{
320
					mk_log("Process is still alive");
321
				}
322
				if(i != 2) sleep(1);
323
			}
324
			if(!dead){
325
				mk_log("Could not kill the process");
326
				return 6;
327
			}
328
 
5 nishi 329
			srv->stopped = true;
330
			return 0;
331
		}
332
	}
333
	return 1;
334
}
335
 
336
int mk_start_service(const char* name){
337
	int i;
338
	for(i = 0; services[i] != NULL; i++){
339
		if(strcmp(services[i]->name, name) == 0){
340
			struct mk_service* srv = services[i];
341
			char* log = mk_strcat("Starting ", name);
342
			mk_log(log);
343
			free(log);
344
 
345
			bool alive = false;
346
 
347
			FILE* f = fopen(srv->pidfile, "r");
348
			if(f != NULL){
349
				unsigned long long pid;
350
				fscanf(f, "%llu", &pid);
351
				fclose(f);
352
				alive = kill(pid, 0) == 0;
353
			}
354
			if(alive){
355
				mk_log("Process seems to be alive, not starting");
356
				return 2;
357
			}
358
 
359
			char** pargv = malloc(sizeof(*pargv));
360
			pargv[0] = NULL;
361
 
362
			int i;
363
			int incr = 0;
364
			for(i = 0;; i++){
365
				if(srv->exec[i] == 0 || srv->exec[i] == ' '){
366
					char* str = malloc(i - incr + 1);
367
					memcpy(str, srv->exec + incr, i - incr);
368
					str[i - incr] = 0;
369
 
370
					char** oldargv = pargv;
371
					int j;
372
					for(j = 0; oldargv[j] != NULL; j++);
373
					pargv = malloc(sizeof(*pargv) * (j + 2));
374
					for(j = 0; oldargv[j] != NULL; j++) pargv[j] = oldargv[j];
375
					pargv[j] = str;
376
					pargv[j + 1]  = NULL;
377
					free(oldargv);
378
 
379
					incr = i + 1;
380
					if(srv->exec[i] == 0) break;
381
				}
382
			}
383
 
384
			bool fail = false;
385
 
386
			pid_t pid = fork();
387
			if(pid == 0){
388
				int n = open("/dev/null", O_RDWR);
389
				dup2(n, 1);
390
				dup2(n, 2);
391
				execvp(pargv[0], pargv);
392
				_exit(-1);
393
			}else{
394
				int status;
395
				waitpid(pid, &status, 0);
396
				if(WEXITSTATUS(status) != 0) fail = true;
397
			}
398
			for(i = 0; pargv[i] != NULL; i++) free(pargv[i]);
399
			free(pargv);
400
			if(fail){
401
				log = mk_strcat("Failed to start ", name);
402
				mk_log(log);
403
				free(log);
404
				srv->stopped = false;
405
				return 3;
406
			}else{
407
				log = mk_strcat("Started ", name);
408
				mk_log(log);
409
				free(log);
410
				srv->stopped = false;
411
			}
412
 
413
			return 0;
414
		}
415
	}
416
	return 1;
417
}
418
 
419
void mk_start_services(void){
420
	int i;
421
	for(i = 0; services[i] != NULL; i++){
422
		mk_start_service(services[i]->name);
423
	}
424
}
10 nishi 425
 
426
void mk_resurrect_services(void){
427
	int i;
428
	bool re = false;
429
	for(i = 0; services[i] != NULL; i++){
430
		if(!services[i]->stopped){
431
			bool alive = false;
432
 
433
			FILE* f = fopen(services[i]->pidfile, "r");
434
			if(f != NULL){
435
				unsigned long long pid;
436
				fscanf(f, "%llu", &pid);
437
				fclose(f);
438
				alive = kill(pid, 0) == 0;
439
			}
440
			if(!alive){
441
				if(!re){
442
					mk_log("Resurrection");
443
					re = true;
444
				}
445
				mk_start_service(services[i]->name);
446
			}
447
		}
448
	}
449
}