]> git.draconx.ca Git - scripts.git/blob - openexec.c
Add script to convert "JWK"-format RSA keys to normal.
[scripts.git] / openexec.c
1 /*
2  * Copyright (C) 2011 Nick Bowler
3  *
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.
11  *
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.
15  *
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.
19  */
20
21 #define _GNU_SOURCE
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29
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' },
35         { 0 }
36 };
37
38 static void print_version(void)
39 {
40         puts("openexec version 1.0");
41         puts("\
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.\
46         ");
47 }
48
49 static void print_usage(FILE *f)
50 {
51         fprintf(f, "Usage: %s [options] [file ...] -- program [arguments]\n",
52                    progname);
53 }
54
55 static void print_help(void)
56 {
57         print_usage(stdout);
58         puts("Detailed help coming eventually.");
59 }
60
61 static char *fake_open(const char *filename)
62 {
63         char *buf;
64         int n, fd;
65
66         fd = open(filename, O_RDONLY);
67         if (fd == -1) {
68                 fprintf(stderr, "%s: %s: %s\n", progname, filename,
69                                                 strerror(errno));
70                 return NULL;
71         }
72
73         n = snprintf(NULL, 0, "/proc/self/fd/%d", fd);
74         buf = malloc(n+1);
75         if (!buf) {
76                 close(fd);
77                 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
78                 return NULL;
79         }
80
81         sprintf(buf, "/proc/self/fd/%d", fd);
82         return buf;
83 }
84
85 int main(int argc, char **argv)
86 {
87         int nargs = 0, opt, rc, err, fds[2];
88
89         if (argc > 0)
90                 progname = argv[0];
91
92         char *args[argc];
93
94         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
95                 switch (opt) {
96                 case 1:
97                         args[nargs] = fake_open(optarg);
98                         if (!args[nargs])
99                                 return EXIT_FAILURE;
100                         nargs++;
101                         break;
102                 case 'V':
103                         print_version();
104                         return EXIT_SUCCESS;
105                 case 'H':
106                         print_help();
107                         return EXIT_SUCCESS;
108                 default:
109                         print_usage(stderr);
110                         return EXIT_FAILURE;
111                 }
112         }
113
114         if (!argv[optind]) {
115                 print_usage(stderr);
116                 return EXIT_FAILURE;
117         }
118
119         args[nargs] = NULL;
120         memmove(args + (argc - optind), args, (nargs + 1) * sizeof *args);
121         memcpy(args, argv + optind, (argc - optind) * sizeof *args);
122
123         /*
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
128          * an error message.
129          */
130         rc = pipe2(fds, O_CLOEXEC);
131         if (rc == -1) {
132                 fprintf(stderr, "%s: pipe2: %s\n", progname, strerror(errno));
133                 return EXIT_FAILURE;
134         }
135
136         switch (fork()) {
137         case -1:
138                 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
139                 return EXIT_FAILURE;
140         case 0:
141                 execvp(args[0], args);
142
143                 /* This horrible if statement shuts up GCC... */
144                 err = errno;
145                 if (write(fds[1], &err, sizeof err))
146                         ;
147                 _Exit(EXIT_FAILURE);
148         }
149
150         close(fds[1]);
151         rc = read(fds[0], &err, sizeof err);
152         if (rc == -1) {
153                 /* No information about the child's state. */
154                 fprintf(stderr, "%s: read: %s\n", progname, strerror(errno));
155                 return EXIT_FAILURE;
156         } else if (rc == 0) {
157                 /* Exec was successful, so we are too. */
158                 return EXIT_SUCCESS;
159         }
160
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));
163         return EXIT_FAILURE;
164 }