diff options
Diffstat (limited to 'src/firecfg/main.c')
-rw-r--r-- | src/firecfg/main.c | 195 |
1 files changed, 188 insertions, 7 deletions
diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 70d29a3ed..15ee78384 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c | |||
@@ -24,8 +24,13 @@ | |||
24 | #include <dirent.h> | 24 | #include <dirent.h> |
25 | #include <sys/types.h> | 25 | #include <sys/types.h> |
26 | #include <sys/stat.h> | 26 | #include <sys/stat.h> |
27 | #include <fcntl.h> | ||
27 | #include <unistd.h> | 28 | #include <unistd.h> |
29 | #include <string.h> | ||
30 | #include <errno.h> | ||
31 | #include <sys/mman.h> | ||
28 | #include "../include/common.h" | 32 | #include "../include/common.h" |
33 | static int arg_debug = 0; | ||
29 | 34 | ||
30 | static void usage(void) { | 35 | static void usage(void) { |
31 | printf("firecfg - version %s\n\n", VERSION); | 36 | printf("firecfg - version %s\n\n", VERSION); |
@@ -37,8 +42,10 @@ static void usage(void) { | |||
37 | printf("DESKTOP INTEGRATION section in man 1 firejail.\n\n"); | 42 | printf("DESKTOP INTEGRATION section in man 1 firejail.\n\n"); |
38 | printf("Usage: firecfg [OPTIONS]\n\n"); | 43 | printf("Usage: firecfg [OPTIONS]\n\n"); |
39 | printf(" --clean - remove all firejail symbolic links.\n\n"); | 44 | printf(" --clean - remove all firejail symbolic links.\n\n"); |
45 | printf(" --debug - print debug messages.\n\n"); | ||
40 | printf(" --help, -? - this help screen.\n\n"); | 46 | printf(" --help, -? - this help screen.\n\n"); |
41 | printf(" --list - list all firejail symbolic links.\n\n"); | 47 | printf(" --list - list all firejail symbolic links.\n\n"); |
48 | printf(" --fix - fix .desktop files.\n\n"); | ||
42 | printf(" --version - print program version and exit.\n\n"); | 49 | printf(" --version - print program version and exit.\n\n"); |
43 | printf("Example:\n\n"); | 50 | printf("Example:\n\n"); |
44 | printf(" $ sudo firecfg\n"); | 51 | printf(" $ sudo firecfg\n"); |
@@ -49,10 +56,14 @@ static void usage(void) { | |||
49 | printf(" /usr/local/bin/firefox\n"); | 56 | printf(" /usr/local/bin/firefox\n"); |
50 | printf(" /usr/local/bin/vlc\n"); | 57 | printf(" /usr/local/bin/vlc\n"); |
51 | printf(" [...]\n"); | 58 | printf(" [...]\n"); |
52 | printf(" $ sudo firecfg --clear\n"); | 59 | printf(" $ sudo firecfg --clean\n"); |
53 | printf(" /usr/local/bin/firefox removed\n"); | 60 | printf(" /usr/local/bin/firefox removed\n"); |
54 | printf(" /usr/local/bin/vlc removed\n"); | 61 | printf(" /usr/local/bin/vlc removed\n"); |
55 | printf(" [...]\n"); | 62 | printf(" [...]\n"); |
63 | printf(" $ firecfg --fix\n"); | ||
64 | printf(" /home/user/.local/share/applications/chromium.desktop created\n"); | ||
65 | printf(" /home/user/.local/share/applications/vlc.desktop created\n"); | ||
66 | printf(" [...]\n"); | ||
56 | printf("\n"); | 67 | printf("\n"); |
57 | printf("License GPL version 2 or later\n"); | 68 | printf("License GPL version 2 or later\n"); |
58 | printf("Homepage: http://firejail.wordpress.com\n\n"); | 69 | printf("Homepage: http://firejail.wordpress.com\n\n"); |
@@ -67,9 +78,12 @@ static int find(const char *program, const char *directory) { | |||
67 | errExit("asprintf"); | 78 | errExit("asprintf"); |
68 | 79 | ||
69 | struct stat s; | 80 | struct stat s; |
70 | if (stat(fname, &s) == 0) | 81 | if (stat(fname, &s) == 0) { |
82 | if (arg_debug) | ||
83 | printf("found %s in directory %s\n", program, directory); | ||
71 | retval = 1; | 84 | retval = 1; |
72 | 85 | } | |
86 | |||
73 | free(fname); | 87 | free(fname); |
74 | return retval; | 88 | return retval; |
75 | } | 89 | } |
@@ -79,7 +93,8 @@ static int find(const char *program, const char *directory) { | |||
79 | static int which(const char *program) { | 93 | static int which(const char *program) { |
80 | // check some well-known paths | 94 | // check some well-known paths |
81 | if (find(program, "/bin") || find(program, "/usr/bin") || | 95 | if (find(program, "/bin") || find(program, "/usr/bin") || |
82 | find(program, "/sbin") || find(program, "/usr/sbin")) | 96 | find(program, "/sbin") || find(program, "/usr/sbin") || |
97 | find(program, "/usr/games")) | ||
83 | return 1; | 98 | return 1; |
84 | 99 | ||
85 | // check environment | 100 | // check environment |
@@ -205,8 +220,9 @@ static void set_file(const char *name, const char *firejail_exec) { | |||
205 | errExit("asprintf"); | 220 | errExit("asprintf"); |
206 | 221 | ||
207 | struct stat s; | 222 | struct stat s; |
208 | if (stat(fname, &s) == 0) | 223 | if (stat(fname, &s) == 0) { |
209 | ; //printf("%s already present\n", fname); | 224 | printf("%s is already present, skipping...\n", fname); |
225 | } | ||
210 | else { | 226 | else { |
211 | int rv = symlink(firejail_exec, fname); | 227 | int rv = symlink(firejail_exec, fname); |
212 | if (rv) { | 228 | if (rv) { |
@@ -268,7 +284,7 @@ static void set(void) { | |||
268 | // empty line | 284 | // empty line |
269 | if (*start == '\0') | 285 | if (*start == '\0') |
270 | continue; | 286 | continue; |
271 | 287 | ||
272 | // set link | 288 | // set link |
273 | set_file(start, firejail_exec); | 289 | set_file(start, firejail_exec); |
274 | } | 290 | } |
@@ -278,6 +294,165 @@ static void set(void) { | |||
278 | free(firejail_exec); | 294 | free(firejail_exec); |
279 | } | 295 | } |
280 | 296 | ||
297 | static void fix_desktop_files(void) { | ||
298 | if (getuid() == 0) { | ||
299 | fprintf(stderr, "Error: you should run --fix as user\n"); | ||
300 | exit(1); | ||
301 | } | ||
302 | |||
303 | char *homedir = getenv("HOME"); | ||
304 | if (!homedir) | ||
305 | errExit("getenv"); | ||
306 | |||
307 | char *user_apps_dir; | ||
308 | if (asprintf(&user_apps_dir, "%s/.local/share/applications", homedir) == -1) | ||
309 | errExit("asprintf"); | ||
310 | |||
311 | DIR *dir = opendir("/usr/share/applications"); | ||
312 | if (!dir) { | ||
313 | perror("Error: cannot open /usr/share/applications directory"); | ||
314 | exit(1); | ||
315 | } | ||
316 | |||
317 | if (chdir("/usr/share/applications")) { | ||
318 | perror("Error: cannot chdir to /usr/share/applications"); | ||
319 | exit(1); | ||
320 | } | ||
321 | |||
322 | struct dirent *entry; | ||
323 | while ((entry = readdir(dir)) != NULL) { | ||
324 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) | ||
325 | continue; | ||
326 | |||
327 | // skip if not regular file or link | ||
328 | if (entry->d_type != DT_REG && entry->d_type != DT_LNK) | ||
329 | continue; | ||
330 | |||
331 | // skip if not .desktop file | ||
332 | if (strstr(entry->d_name,".desktop") != (entry->d_name+strlen(entry->d_name)-8)) | ||
333 | continue; | ||
334 | |||
335 | char *filename = entry->d_name; | ||
336 | |||
337 | // skip links | ||
338 | if (is_link(filename)) | ||
339 | continue; | ||
340 | |||
341 | struct stat sb; | ||
342 | if (stat(filename, &sb) == -1) | ||
343 | errExit("stat"); | ||
344 | |||
345 | /* coverity[toctou] */ | ||
346 | int fd = open(filename, O_RDONLY); | ||
347 | if (fd == -1) | ||
348 | errExit("open"); | ||
349 | |||
350 | char *buf = mmap(NULL, sb.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
351 | if (buf == MAP_FAILED) | ||
352 | errExit("mmap"); | ||
353 | |||
354 | close(fd); | ||
355 | |||
356 | // check format | ||
357 | if (strstr(buf, "[Desktop Entry]\n") == NULL) { | ||
358 | if (arg_debug) | ||
359 | fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); | ||
360 | munmap(buf, sb.st_size + 1); | ||
361 | continue; | ||
362 | } | ||
363 | |||
364 | // get executable name | ||
365 | char *ptr1 = strstr(buf,"\nExec="); | ||
366 | if (!ptr1 || strlen(ptr1) < 7) { | ||
367 | if (arg_debug) | ||
368 | fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); | ||
369 | munmap(buf, sb.st_size + 1); | ||
370 | continue; | ||
371 | } | ||
372 | |||
373 | char *execname = ptr1 + 6; | ||
374 | // https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | ||
375 | // The executable program can either be specified with its full path | ||
376 | // or with the name of the executable only | ||
377 | if (execname[0] != '/') { | ||
378 | if (arg_debug) | ||
379 | fprintf(stderr, "/usr/share/applications/%s - already OK\n", filename); | ||
380 | continue; | ||
381 | } | ||
382 | // executable name can be quoted, this is rare and currently unsupported, TODO | ||
383 | if (execname[0] == '"') { | ||
384 | if (arg_debug) | ||
385 | fprintf(stderr, "/usr/share/applications/%s - skipped: path quoting unsupported\n", filename); | ||
386 | continue; | ||
387 | } | ||
388 | |||
389 | // put '\0' at end of filename | ||
390 | char *tail = NULL; | ||
391 | char endchar = ' '; | ||
392 | if (execname[0] == '/') { | ||
393 | char *ptr2 = index(execname, ' '); | ||
394 | char *ptr3 = index(execname, '\n'); | ||
395 | if (ptr2 && (!ptr3 || (ptr2 < ptr3))) { | ||
396 | endchar = ptr2[0]; | ||
397 | ptr2[0] = '\0'; | ||
398 | tail = ptr2 + 1; | ||
399 | } else if (ptr3 && (!ptr2 || (ptr3 < ptr2))) { | ||
400 | endchar = ptr3[0]; | ||
401 | ptr3[0] = '\0'; | ||
402 | tail = ptr3 + 1; | ||
403 | } | ||
404 | ptr1[5] = '\0'; | ||
405 | } | ||
406 | |||
407 | char *bname = basename(execname); | ||
408 | assert(bname); | ||
409 | |||
410 | // check if basename in PATH | ||
411 | if (!which(bname)) { | ||
412 | fprintf(stderr, "/usr/share/applications/%s - skipped, %s not in PATH\n", filename, bname); | ||
413 | continue; | ||
414 | } | ||
415 | |||
416 | char *outname; | ||
417 | if (asprintf(&outname ,"%s/%s", user_apps_dir, filename) == -1) | ||
418 | errExit("asprintf"); | ||
419 | |||
420 | int fd1 = open(outname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); | ||
421 | free(outname); | ||
422 | |||
423 | if (fd1 == -1) { | ||
424 | fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); | ||
425 | munmap(buf, sb.st_size + 1); | ||
426 | continue; | ||
427 | } | ||
428 | |||
429 | FILE *outfile = fdopen(fd1, "w"); | ||
430 | if (!outfile) { | ||
431 | fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); | ||
432 | munmap(buf, sb.st_size + 1); | ||
433 | close(fd1); | ||
434 | continue; | ||
435 | } | ||
436 | |||
437 | if (fprintf(outfile,\ | ||
438 | "# Converted by firecfg --fix from /usr/share/applications/%s\n\n%s=%s%c%s",\ | ||
439 | filename, buf, bname, endchar, tail) < 0) { | ||
440 | fprintf(stderr, "Unable to write %s/%s: %s\n", user_apps_dir, filename, strerror(errno)); | ||
441 | munmap(buf, sb.st_size + 1); | ||
442 | fclose(outfile); | ||
443 | continue; | ||
444 | } | ||
445 | |||
446 | fclose(outfile); | ||
447 | munmap(buf, sb.st_size + 1); | ||
448 | |||
449 | printf("%s/%s created\n", user_apps_dir, filename); | ||
450 | } | ||
451 | |||
452 | closedir(dir); | ||
453 | free(user_apps_dir); | ||
454 | } | ||
455 | |||
281 | int main(int argc, char **argv) { | 456 | int main(int argc, char **argv) { |
282 | int i; | 457 | int i; |
283 | 458 | ||
@@ -288,6 +463,8 @@ int main(int argc, char **argv) { | |||
288 | usage(); | 463 | usage(); |
289 | return 0; | 464 | return 0; |
290 | } | 465 | } |
466 | else if (strcmp(argv[i], "--debug") == 0) | ||
467 | arg_debug = 1; | ||
291 | else if (strcmp(argv[i], "--version") == 0) { | 468 | else if (strcmp(argv[i], "--version") == 0) { |
292 | printf("firecfg version %s\n\n", VERSION); | 469 | printf("firecfg version %s\n\n", VERSION); |
293 | return 0; | 470 | return 0; |
@@ -300,6 +477,10 @@ int main(int argc, char **argv) { | |||
300 | list(); | 477 | list(); |
301 | return 0; | 478 | return 0; |
302 | } | 479 | } |
480 | else if (strcmp(argv[i], "--fix") == 0) { | ||
481 | fix_desktop_files(); | ||
482 | return 0; | ||
483 | } | ||
303 | else { | 484 | else { |
304 | fprintf(stderr, "Error: invalid command line option\n"); | 485 | fprintf(stderr, "Error: invalid command line option\n"); |
305 | usage(); | 486 | usage(); |