]> git.ipfire.org Git - ipfire-2.x.git/blob - src/misc-progs/addonctrl.c
suricata: Change midstream policy to "pass-flow"
[ipfire-2.x.git] / src / misc-progs / addonctrl.c
1 /* This file is part of the IPFire Firewall.
2 *
3 * This program is distributed under the terms of the GNU General Public
4 * Licence. See the file COPYING for details.
5 *
6 */
7
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <dirent.h>
16 #include <fnmatch.h>
17 #include <errno.h>
18 #include "setuid.h"
19
20 #define BUFFER_SIZE 1024
21
22 const char *initd_path = "/etc/rc.d/init.d";
23 const char *enabled_path = "/etc/rc.d/rc3.d";
24 const char *disabled_path = "/etc/rc.d/rc3.d/off";
25
26 const char *usage =
27 "Usage\n"
28 " addonctrl <addon> (start|stop|restart|reload|enable|disable|status|boot-status|list-services) [<service>]\n"
29 "\n"
30 "Options:\n"
31 " <addon>\t\tName of the addon to control\n"
32 " <service>\t\tSpecific service of the addon to control (optional)\n"
33 " \t\t\tBy default the requested action is performed on all related services. See also 'list-services'.\n"
34 " start\t\t\tStart service(s) of the addon\n"
35 " stop\t\t\tStop service(s) of the addon\n"
36 " restart\t\tRestart service(s) of the addon\n"
37 " enable\t\tEnable service(s) of the addon to start at boot\n"
38 " disable\t\tDisable service(s) of the addon to start at boot\n"
39 " status\t\tDisplay current state of addon service(s)\n"
40 " boot-status\t\tDisplay wether service(s) is enabled on boot or not\n"
41 " list-services\t\tDisplay a list of services related to the addon";
42
43 // Find a file <path> using <filepattern> as glob pattern.
44 // Returns the found filename or NULL if not found
45 char *find_file_in_dir(const char *path, const char *filepattern)
46 {
47 struct dirent *entry;
48 DIR *dp;
49 char *found = NULL;
50
51 dp = opendir(path);
52 if (dp) {
53 entry = readdir(dp);
54 while(!found && entry) {
55 if (fnmatch(filepattern, entry->d_name, FNM_PATHNAME) == 0)
56 found = strdup(entry->d_name);
57 else
58 entry = readdir(dp);
59 }
60
61 closedir(dp);
62 }
63
64 return found;
65 }
66
67 // Reads Services metadata for <addon>.
68 // Returns pointer to array of strings containing the services for <addon>,
69 // sets <servicescnt> to the number of found services and
70 // sets <returncode> to
71 // -1 - system error occured, check errno
72 // 0 - success - if returned array is NULL, there are no services for <addon>
73 // 1 - addon was not found
74 char **get_addon_services(const char *addon, int *servicescnt, const char *filter, int *returncode) {
75 const char *metafile_prefix = "/opt/pakfire/db/installed/meta-";
76 const char *metadata_key = "Services";
77 const char *keyvalue_delim = ":";
78 const char *service_delim = " ";
79 char *token;
80 char **services = NULL;
81 char *service;
82 char *line = NULL;
83 size_t line_len = 0;
84 int i = 0;
85 char *metafile = NULL;
86
87 *returncode = 0;
88
89 if (!addon) {
90 errno = EINVAL;
91 *returncode = 1;
92 return NULL;
93 }
94
95 *returncode = asprintf(&metafile, "%s%s", metafile_prefix, addon);
96 if (*returncode == -1)
97 return NULL;
98
99 FILE *fp = fopen(metafile,"r");
100 if (!fp) {
101 if (errno == ENOENT) {
102 *returncode = 1;
103 } else {
104 *returncode = -1;
105 }
106 return NULL;
107 }
108
109 // Get initscript(s) for addon from meta-file
110 while (getline(&line, &line_len, fp) != -1 && !services) {
111 // Strip newline
112 char *newline = strchr(line, '\n');
113 if (newline) *newline = 0;
114
115 // Split line in key and values; Check for required key.
116 token = strtok(line, keyvalue_delim);
117 if (!token || strcmp(token, metadata_key) != 0)
118 continue;
119
120 // Get values for matched key. Stop if no values are present.
121 token = strtok(NULL, keyvalue_delim);
122 if (!token)
123 break;
124
125 // Split values and put each service in services array
126 service = strtok(token, service_delim);
127 while (service) {
128 if (!filter || strcmp(filter, service) == 0) {
129 services = reallocarray(services, i+1 ,sizeof (char *));
130 if (!services) {
131 *returncode = -1;
132 break;
133 }
134
135 services[i] = strdup(service);
136 if (!services[i++]) {
137 *returncode = -1;
138 break;
139 }
140 }
141
142 service = strtok(NULL, service_delim);
143 }
144 }
145
146 if (line) free(line);
147 fclose(fp);
148 free(metafile);
149
150 *servicescnt = i;
151
152 return services;
153 }
154
155 // Calls initscript <service> with parameter <action>
156 int initscript_action(const char *service, const char *action) {
157 char *initscript = NULL;
158 char *argv[] = {
159 action,
160 NULL
161 };
162 int r = 0;
163
164 r = asprintf(&initscript, "%s/%s", initd_path, service);
165 if (r != -1)
166 r = run(initscript, argv);
167
168 if (initscript) free(initscript);
169
170 return r;
171 }
172
173 // Move an initscript with filepattern from <src_path> to <dest_path>
174 // Returns:
175 // -1: Error during move or memory allocation. Details in errno
176 // 0: Success
177 // 1: file was not moved, but is already in <dest_path>
178 // 2: file does not exist in either in <src_path> or <dest_path>
179 int move_initscript_by_pattern(const char *src_path, const char *dest_path, const char *filepattern) {
180 char *src = NULL;
181 char *dest = NULL;
182 int r = 2;
183 char *filename = NULL;
184
185 filename = find_file_in_dir(src_path, filepattern);
186 if (filename) {
187 // Move file
188 r = asprintf(&src, "%s/%s", src_path, filename);
189 if (r != -1) {
190 r = asprintf(&dest, "%s/%s", dest_path, filename);
191 if (r != -1)
192 r = rename(src, dest);
193 }
194
195 if (src) free(src);
196 if (dest) free(dest);
197 } else {
198 // check if file is already in dest
199 filename = find_file_in_dir(dest_path, filepattern);
200 if (filename)
201 r = 1;
202 }
203
204 if (filename) free(filename);
205
206 return r;
207 }
208
209 // Enable/Disable addon service(s) by moving initscript symlink from/to disabled_path
210 // Returns:
211 // -1 - System error occured. Check errno.
212 // 0 - Success
213 // 1 - Service was already enabled/disabled
214 // 2 - Service has no valid runlevel symlink
215 int toggle_service(const char *service, const char *action) {
216 const char *src_path, *dest_path;
217 char *filepattern = NULL;
218 int r = 0;
219
220 if (asprintf(&filepattern, "S??%s", service) == -1)
221 return -1;
222
223 if (strcmp(action, "enable") == 0) {
224 src_path = disabled_path;
225 dest_path = enabled_path;
226 } else {
227 src_path = enabled_path;
228 dest_path = disabled_path;
229 }
230
231 // Ensure disabled_path exists
232 r = mkdir(disabled_path, S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH);
233 if (r != -1 || errno == EEXIST)
234 r = move_initscript_by_pattern(src_path, dest_path, filepattern);
235
236 free(filepattern);
237
238 return r;
239 }
240
241 // Return whether <service> is enabled or disabled on boot
242 // Returns:
243 // -1 - System error occured. Check errno.
244 // 0 - <service> is disabled on boot
245 // 1 - <service> is enabled on boot
246 // 2 - Runlevel suymlink for <service> was not found
247 int get_boot_status(char *service) {
248 char *filepattern = NULL;
249 char *filename = NULL;
250 int r = 2;
251
252 if (asprintf(&filepattern, "S??%s", service) == -1)
253 return -1;
254
255 filename = find_file_in_dir(enabled_path, filepattern);
256 if (filename)
257 r = 1;
258 else {
259 filename = find_file_in_dir(disabled_path, filepattern);
260 if (filename)
261 r = 0;
262 else
263 r = 2;
264 }
265
266 if (filename) free(filename);
267 free(filepattern);
268
269 return r;
270 }
271
272 int main(int argc, char *argv[]) {
273 char **services = NULL;
274 int servicescnt = 0;
275 char *addon = argv[1];
276 char *action = argv[2];
277 char *service_filter = NULL;
278 int r = 0;
279
280 if (!(initsetuid()))
281 exit(1);
282
283 if (argc < 3) {
284 fprintf(stderr, "\nMissing arguments.\n\n%s\n\n", usage);
285 exit(1);
286 }
287
288 // Ignore filter when list of services is requested
289 if (argc == 4 && strcmp(action, "list-services") != 0)
290 service_filter = argv[3];
291
292 if (strlen(addon) > 32) {
293 fprintf(stderr, "\nString too large.\n\n%s\n\n", usage);
294 exit(1);
295 }
296
297 // Check if the input argument is valid
298 if (!is_valid_argument_alnum(addon)) {
299 fprintf(stderr, "Invalid add-on name: %s.\n", addon);
300 exit(2);
301 }
302
303 // Get initscript name(s) from addon metadata
304 int rc = 0;
305 services = get_addon_services(addon, &servicescnt, service_filter, &rc);
306 if (!services) {
307 switch (rc) {
308 case -1:
309 fprintf(stderr, "\nSystem error occured. (Error: %m)\n\n");
310 break;
311
312 case 0:
313 if (service_filter)
314 fprintf(stderr, "\nNo service '%s' found for addon '%s'. Use 'list-services' to get a list of available services\n\n%s\n\n", service_filter, addon, usage);
315 else
316 fprintf(stderr, "\nAddon '%s' has no services.\n\n", addon);
317 break;
318
319 case 1:
320 fprintf(stderr, "\nAddon '%s' not found.\n\n%s\n\n", addon, usage);
321 break;
322 }
323 exit(1);
324 }
325
326 // Handle requested action
327 if (strcmp(action, "start") == 0 ||
328 strcmp(action, "stop") == 0 ||
329 strcmp(action, "restart") == 0 ||
330 strcmp(action, "reload") == 0 ||
331 strcmp(action, "status") == 0) {
332
333 for(int i = 0; i < servicescnt; i++) {
334 if (initscript_action(services[i], action) < 0) {
335 r = 1;
336 fprintf(stderr, "\nSystem error occured. (Error: %m)\n\n");
337 break;
338 }
339 }
340
341 } else if (strcmp(action, "enable") == 0 ||
342 strcmp(action, "disable") == 0) {
343
344 for(int i = 0; i < servicescnt; i++) {
345 switch (r = toggle_service(services[i], action)) {
346 case -1:
347 fprintf(stderr, "\nSystem error occured. (Error: %m)\n\n");
348 break;
349
350 case 0:
351 printf("%sd service %s\n", action, services[i]);
352 break;
353
354 case 1:
355 fprintf(stderr, "Service %s is already %sd. Skipping...\n", services[i], action);
356 break;
357
358 case 2:
359 fprintf(stderr, "\nUnable to %s service %s. (Service has no valid runlevel symlink).\n\n", action, services[i]);
360 break;
361 }
362
363 // Break from for loop in case of a system error.
364 if (r == -1) {
365 r = 1;
366 break;
367 }
368 }
369
370 } else if (strcmp(action, "boot-status") == 0) {
371 // Print boot status for each service
372 for(int i = 0; i < servicescnt; i++) {
373 switch (get_boot_status(services[i])) {
374 case -1:
375 r = 1;
376 fprintf(stderr, "\nSystem error occured. (Error: %m)\n\n");
377 break;
378
379 case 0:
380 printf("%s is disabled on boot.\n", services[i]);
381 break;
382
383 case 1:
384 printf("%s is enabled on boot.\n", services[i]);
385 break;
386
387 case 2:
388 printf("%s is not available for boot. (Service has no valid symlink in either %s or %s).\n", services[i], enabled_path, disabled_path);
389 break;
390 }
391
392 // Break from for loop in case of an error
393 if (r == 1) {
394 break;
395 }
396 }
397
398 } else if (strcmp(action, "list-services") == 0) {
399 // List all services for addon
400 printf("\nServices for addon %s:\n", addon);
401 for(int i = 0; i < servicescnt; i++) {
402 printf(" %s\n", services[i]);
403 }
404 printf("\n");
405
406 } else {
407 fprintf(stderr, "\nBad argument given.\n\n%s\n\n", usage);
408 r = 1;
409 }
410
411 // Cleanup
412 for(int i = 0; i < servicescnt; i++)
413 free(services[i]);
414 free(services);
415
416 return r;
417 }