2 * Copyright (C) 2011 Nick Bowler
4 * The Mutt email client opens mail attachments with external programs
5 * synchronously: the attachment is decoded and saved to /tmp, the filename
6 * is passed to the program to be run, and when the program exits, the file
7 * is unlinked. This leads to an obvious problem: if the attachment-viewing
8 * program runs in the foreground, one cannot interact with mutt until you
9 * quit it. If the program runs in the background, then there is a race
10 * between the program opening the file and mutt unlinking it.
12 * This tool solves this problem by opening all files and spawning a program
13 * in the background. It is safe to unlink the files after they are opened,
14 * so the race with mutt is avoided.
16 * License WTFPL2: Do What The Fuck You Want To Public License, version 2.
17 * This is free software: you are free to do what the fuck you want to.
18 * There is NO WARRANTY, to the extent permitted by law.
30 static const char *progname = "openexec";
31 static const char sopts[] = "-VH";
32 static const struct option lopts[] = {
33 { "version", 0, NULL, 'V' },
34 { "help", 0, NULL, 'H' },
38 static void print_version(void)
40 puts("openexec version 1.0");
42 Copyright (C) 2011 Nick Bowler.\n\
43 License WTFPL2: Do What The Fuck You Want To Public License, version 2.\n\
44 This is free software: you are free to do what the fuck you want to.\n\
45 There is NO WARRANTY, to the extent permitted by law.\
49 static void print_usage(FILE *f)
51 fprintf(f, "Usage: %s [options] [file ...] -- program [arguments]\n",
55 static void print_help(void)
58 puts("Detailed help coming eventually.");
61 static char *fake_open(const char *filename)
66 fd = open(filename, O_RDONLY);
68 fprintf(stderr, "%s: %s: %s\n", progname, filename,
73 n = snprintf(NULL, 0, "/proc/self/fd/%d", fd);
77 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
81 sprintf(buf, "/proc/self/fd/%d", fd);
85 int main(int argc, char **argv)
87 int nargs = 0, opt, rc, err, fds[2];
94 while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
97 args[nargs] = fake_open(optarg);
120 memmove(args + (argc - optind), args, (nargs + 1) * sizeof *args);
121 memcpy(args, argv + optind, (argc - optind) * sizeof *args);
124 * We use a close-on-exec pipe for the child to signal success/failure
125 * to the parent. On successful exec, the write end of the pipe will
126 * be closed, causing the read in the parent to return 0. On failure,
127 * errno will be written to the pipe, allowing the parent to print
130 rc = pipe2(fds, O_CLOEXEC);
132 fprintf(stderr, "%s: pipe2: %s\n", progname, strerror(errno));
138 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
141 execvp(args[0], args);
143 /* This horrible if statement shuts up GCC... */
145 if (write(fds[1], &err, sizeof err))
151 rc = read(fds[0], &err, sizeof err);
153 /* No information about the child's state. */
154 fprintf(stderr, "%s: read: %s\n", progname, strerror(errno));
156 } else if (rc == 0) {
157 /* Exec was successful, so we are too. */
161 /* Exec in the child failed, print the error we received on the pipe. */
162 fprintf(stderr, "%s: %s: %s\n", progname, args[0], strerror(err));