chiark / gitweb /
037bd8644e271675b6817b019c7af4d23cc97dac
[chiark-utils.git] / cprogs / cgi-fcgi-perl.c
1 /*
2  * "Interpreter" that you can put in #! like this
3  *   #!/usr/bin/cgi-fcgi-perl [<options>]
4  *
5  * The result is a program which looks, when executed via the #!
6  * line, like a CGI program.  But the perl script inside will
7  * be executed via /usr/bin/perl in fcgi.
8  *
9  * Options:
10  *
11  *  -g<ident>
12  *          Use <ident> rather than hex(sha256(<script>))
13  *          as the basename of the leafname of the fcgi rendezvous
14  *          socket.  If <ident> contains only hex digit characters it
15  *          ought to be no more than 32 characters.
16  *
17  *  -M<numservers>
18  *         Start <numservers> instances of the program.  This
19  *         determines the maximum concurrency.  (Note that unlike
20  *         speedy, the specified number of servers is started
21  *         right away.)  The default is 4.
22  *
23  * cgi-fcgi-perl automatically expires old sockets, including
24  * ones where the named perl script is out of date.
25  */
26
27 /*
28  * Uses one of two directories
29  *   /var/run/user/<UID>/cgi-fcgi-perl/
30  *   ~/.cgi-fcgi-perl/<node>/
31  * and inside there uses these paths
32  *   s<ident>
33  *   g<inum>
34  *
35  * If -M<ident> is not specified then an initial substricg of the
36  * lowercase hex of the sha256 of the <script> (ie, our argv[1]) is
37  * used.  The substring is chosen so that the whole path is 10 bytes
38  * shorter than sizeof(sun_path).  But always at least 33 characters.
39  *
40  * Algorithm:
41  *  - see if /var/run/user exists
42  *       if so, lstat /var/run/user/<UID> and check that
43  *         we own it and it's X700; if not, fail
44  *         if it's ok then <base> is /var/run/user/<UID>
45  *       otherwise, look for and maybe create ~/.cgi-fcgi-perl
46  *         (where ~ is HOME or from getpwuid)
47  *         and then <base> is ~/.cgi-fcgi-perl/<node>
48  *  - calculate pathname (checking <ident> length is OK)
49  *  - check for and maybe create <base>
50  *  - stat and lstat the <script>
51  *  - stat the socket and check its timestamp
52  *       if it is too hold, rename it to g<inum> (where
53  *       <inum> is in decimal)
54  *       and run garbage collection
55  *  - run  cgi-fcgi -connect SOCKET SCRIPT
56  */
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <errno.h>
62
63 #include "myopt.h"
64
65 static const char *ident;
66 static int numservers;
67
68 static void diee(const char *m) {
69   fprintf(stderr,"cgi-fcgi-: error: %s failed: %s\n", m, strerror(errno));
70   exit(127);
71 }
72
73 static void fusagemessage(FILE *f) {
74   fprintf(f, "usage: #!/usr/bin/cgi-fcgi-perl [<options>]\n");
75 }
76
77 void usagemessage(void) { fusagemessage(stderr); }
78
79 static void of_help(const struct cmdinfo *ci, const char *val) {
80   fusagemessage(stdout);
81   if (ferror(stdout)) diee("write usage message to stdout");
82   exit(0);
83 }
84
85 static const struct cmdinfo cmdinfos[]= {
86   { "help",   0, .call= of_help               },
87   { 0, 'g',   1, .sassignto= &ident           },
88   { 0, 'M',   1, .iassignto= &numservers      },
89   { 0 }
90 };
91
92 int main(int argc, const char *const *argv) {
93   myopt(&argv, cmdinfos);
94   exit(0);
95 }