From c08a57c8811cbb2868ab448de5b7e7a68e6b0bb4 Mon Sep 17 00:00:00 2001 From: Aleksey Manevich Date: Fri, 2 Sep 2016 14:22:01 +0300 Subject: firecfg: option to fix .desktop files --- src/firecfg/main.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) (limited to 'src/firecfg') diff --git a/src/firecfg/main.c b/src/firecfg/main.c index 9c7c49e1d..8ca56a2ff 100644 --- a/src/firecfg/main.c +++ b/src/firecfg/main.c @@ -24,7 +24,11 @@ #include #include #include +#include #include +#include +#include +#include #include "../include/common.h" static int arg_debug = 0; @@ -41,6 +45,7 @@ static void usage(void) { printf(" --debug - print debug messages.\n\n"); printf(" --help, -? - this help screen.\n\n"); printf(" --list - list all firejail symbolic links.\n\n"); + printf(" --fix - fix .desktop files.\n\n"); printf(" --version - print program version and exit.\n\n"); printf("Example:\n\n"); printf(" $ sudo firecfg\n"); @@ -55,6 +60,10 @@ static void usage(void) { printf(" /usr/local/bin/firefox removed\n"); printf(" /usr/local/bin/vlc removed\n"); printf(" [...]\n"); + printf(" $ firecfg --fix\n"); + printf(" /home/user/.local/share/applications/chromium.desktop created\n"); + printf(" /home/user/.local/share/applications/vlc.desktop created\n"); + printf(" [...]\n"); printf("\n"); printf("License GPL version 2 or later\n"); printf("Homepage: http://firejail.wordpress.com\n\n"); @@ -285,6 +294,164 @@ static void set(void) { free(firejail_exec); } +static void fix_desktop_files(void) { + if (getuid() == 0) { + fprintf(stderr, "Error: you should run --fix as user\n"); + exit(1); + } + + char *homedir = getenv("HOME"); + if (!homedir) + errExit("getenv"); + + char *user_apps_dir; + if (asprintf(&user_apps_dir, "%s/.local/share/applications", homedir) == -1) + errExit("asprintf"); + + DIR *dir = opendir("/usr/share/applications"); + if (!dir) { + perror("Error: cannot open /usr/share/applications directory"); + exit(1); + } + + if (chdir("/usr/share/applications")) { + perror("Error: cannot chdir to /usr/share/applications"); + exit(1); + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // skip if not regular file or link + if (entry->d_type != DT_REG && entry->d_type != DT_LNK) + continue; + + // skip if not .desktop file + if (strstr(entry->d_name,".desktop") != (entry->d_name+strlen(entry->d_name)-8)) + continue; + + char *filename = entry->d_name; + + // skip links + if (is_link(filename)) + continue; + + struct stat sb; + if (stat(filename, &sb) == -1) + errExit("stat"); + + int fd = open(filename, O_RDONLY); + if (fd == -1) + errExit("open"); + + char *buf = mmap(NULL, sb.st_size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) + errExit("mmap"); + + close(fd); + + // check format + if (strstr(buf, "[Desktop Entry]\n") == NULL) { + if (arg_debug) + fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); + munmap(buf, sb.st_size + 1); + continue; + } + + // get executable name + char *ptr1 = strstr(buf,"\nExec="); + if (!ptr1 || strlen(ptr1) < 7) { + if (arg_debug) + fprintf(stderr, "/usr/share/applications/%s - SKIPPED: wrong format?\n", filename); + munmap(buf, sb.st_size + 1); + continue; + } + + char *execname = ptr1 + 6; + // https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + // The executable program can either be specified with its full path + // or with the name of the executable only + if (execname[0] != '/') { + if (arg_debug) + fprintf(stderr, "/usr/share/applications/%s - already OK\n", filename); + continue; + } + // executable name can be quoted, this is rare and currently unsupported, TODO + if (execname[0] == '"') { + if (arg_debug) + fprintf(stderr, "/usr/share/applications/%s - skipped: path quoting unsupported\n", filename); + continue; + } + + // put '\0' at end of filename + char *tail = NULL; + char endchar; + if (execname[0] == '/') { + char *ptr2 = index(execname, ' '); + char *ptr3 = index(execname, '\n'); + if (ptr2 && (!ptr3 || (ptr2 < ptr3))) { + endchar = ptr2[0]; + ptr2[0] = '\0'; + tail = ptr2 + 1; + } else if (ptr3 && (!ptr2 || (ptr3 < ptr2))) { + endchar = ptr3[0]; + ptr3[0] = '\0'; + tail = ptr3 + 1; + } + ptr1[5] = '\0'; + } + + char *bname = basename(execname); + assert(bname); + + // check if basename in PATH + if (!which(bname)) { + fprintf(stderr, "/usr/share/applications/%s - unable to fix: executable not in PATH\n", filename); + continue; + } + + char *outname; + if (asprintf(&outname ,"%s/%s", user_apps_dir, filename) == -1) + errExit("asprintf"); + + int fd1 = open(outname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); + free(outname); + + if (fd1 == -1) { + fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); + munmap(buf, sb.st_size + 1); + continue; + } + + FILE *outfile = fdopen(fd1, "w"); + if (!outfile) { + fprintf(stderr, "%s/%s skipped: %s\n", user_apps_dir, filename, strerror(errno)); + munmap(buf, sb.st_size + 1); + close(fd1); + continue; + } + + if (fprintf(outfile,\ + "# Converted by firecfg --fix from /usr/share/applications/%s\n\n%s=%s%c%s",\ + filename, buf, bname, endchar, tail) < 0) { + fprintf(stderr, "Unable to write %s/%s: %s\n", user_apps_dir, filename, strerror(errno)); + munmap(buf, sb.st_size + 1); + fclose(outfile); + continue; + } + + fclose(outfile); + munmap(buf, sb.st_size + 1); + + printf("%s/%s created\n", user_apps_dir, filename); + } + + closedir(dir); + free(user_apps_dir); +} + int main(int argc, char **argv) { int i; @@ -309,6 +476,10 @@ int main(int argc, char **argv) { list(); return 0; } + else if (strcmp(argv[i], "--fix") == 0) { + fix_desktop_files(); + return 0; + } else { fprintf(stderr, "Error: invalid command line option\n"); usage(); -- cgit v1.2.3-70-g09d2