1 /* cvm/module_local.c - Local CVM server module loop
2  * Copyright (C) 2010  Bruce Guenter <bruce@untroubled.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #include <sys/types.h>
19 #include <grp.h>
20 #include <pwd.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 
26 #include <bglibs/msg.h>
27 #include <bglibs/socket.h>
28 #include <bglibs/unix.h>
29 
30 #include "module.h"
31 
32 static const char* path;
33 static int sock;
34 static int conn;
35 static unsigned long timeout = 1000;
36 
poll_timeout(int fd,int event,unsigned long * timeout_left)37 static int poll_timeout(int fd, int event, unsigned long* timeout_left)
38 {
39   struct timeval start;
40   struct timeval end;
41   iopoll_fd io;
42   int r;
43 
44   io.fd = fd;
45   io.events = event;
46   gettimeofday(&start, 0);
47   r = iopoll_restart(&io, 1, *timeout_left);
48   gettimeofday(&end, 0);
49   *timeout_left -= (end.tv_usec - start.tv_usec) / 1000
50     + (end.tv_sec - start.tv_sec) * 1000;
51   return r;
52 }
53 
read_input(void)54 static int read_input(void)
55 {
56   unsigned rd;
57   unsigned long timeout_left;
58 
59   if ((conn = socket_acceptu(sock)) == -1) return CVME_IO;
60   if (!nonblock_on(conn)) {
61     close(conn);
62     return CVME_IO;
63   }
64 
65   for (cvm_module_inbuflen = 0, timeout_left = timeout;
66        cvm_module_inbuflen < BUFSIZE;
67        cvm_module_inbuflen += rd) {
68 
69     switch (poll_timeout(conn, IOPOLL_READ, &timeout_left)) {
70     case 0:
71     case -1:
72       close(conn);
73       return CVME_IO;
74     }
75 
76     if ((rd = read(conn, cvm_module_inbuffer+cvm_module_inbuflen,
77 		   BUFSIZE-cvm_module_inbuflen)) == 0)
78       break;
79     if (rd == (unsigned)-1) {
80       close(conn);
81       return CVME_IO;
82     }
83   }
84   return 0;
85 }
86 
write_output(void)87 static void write_output(void)
88 {
89   unsigned wr;
90   unsigned written;
91   unsigned long timeout_left;
92 
93   for (written = 0, timeout_left = timeout;
94        written < cvm_module_outbuflen;
95        written += wr) {
96     if (poll_timeout(conn, IOPOLL_WRITE, &timeout_left) != 1)
97       break;
98     if ((wr = write(conn, cvm_module_outbuffer+written,
99 		    cvm_module_outbuflen-written)) == 0)
100       break;
101     if (wr == (unsigned)-1) break;
102   }
103   close(conn);
104 }
105 
exitfn()106 static void exitfn()
107 {
108   unlink(path);
109   cvm_module_log_shutdown();
110   exit(0);
111 }
112 
make_socket(void)113 static int make_socket(void)
114 {
115   mode_t old_umask;
116   mode_t mode = 0777;
117   uid_t owner = -1;
118   gid_t group = -1;
119   const char* tmp;
120   char* end;
121   struct passwd* pw;
122   struct group* gr;
123 
124   if ((tmp = getenv("CVM_SOCKET_MODE")) != 0)
125     mode = strtoul(tmp, 0, 8);
126   if ((tmp = getenv("CVM_SOCKET_OWNER")) != 0) {
127     owner = strtoul(tmp, &end, 10);
128     if (*end != 0) {
129       if ((pw = getpwnam(tmp)) == 0) {
130 	error1sys("getpwnam failed");
131 	return CVME_IO;
132       }
133       owner = pw->pw_uid;
134       group = pw->pw_gid;
135     }
136   }
137   if ((tmp = getenv("CVM_SOCKET_GROUP")) != 0) {
138     group = strtoul(tmp, &end, 10);
139     if (*end != 0) {
140       if ((gr = getgrnam(tmp)) == 0) {
141 	error1sys("getgrnam failed");
142 	return CVME_IO;
143       }
144       group = gr->gr_gid;
145     }
146   }
147 
148   old_umask = umask((mode & 0777) ^ 0777);
149   if ((sock = socket_unixstr()) == -1)
150     error1sys("Could not create socket");
151   else if (!socket_bindu(sock, path))
152     error1sys("Could not bind socket");
153   else if (chmod(path, mode) == -1)
154     error1sys("Could not change socket permission");
155   else if (chown(path, owner, group) == -1)
156     error1sys("Could not change socket ownership");
157   else if (!socket_listen(sock, 1))
158     error1sys("Could not listen on socket");
159   else {
160     umask(old_umask);
161     return 0;
162   }
163   return CVME_IO;
164 }
165 
166 extern void usage(void);
167 
local_main(const char * p)168 int local_main(const char* p)
169 {
170   int code;
171   const char* e;
172 
173   path = p;
174 
175   signal(SIGPIPE, SIG_IGN);
176   signal(SIGINT, exitfn);
177   signal(SIGTERM, exitfn);
178 
179   if ((e = getenv("CVM_LOCAL_TIMEOUT")) == 0
180       || (timeout = strtoul(e, (char**)&e, 10)) == 0
181       || *e != 0)
182     timeout = DEFAULT_TIMEOUT;
183 
184   if ((code = make_socket()) != 0) return code;
185   if ((code = cvm_module_init()) != 0) return code;
186   cvm_module_log_startup();
187 
188   code = 0;
189   do {
190     if ((code = read_input()) != 0) continue;
191     code = cvm_module_handle_request();
192     cvm_module_fact_end(code & CVME_MASK);
193     cvm_module_log_request();
194     write_output();
195   } while ((code & CVME_FATAL) == 0);
196   cvm_module_stop();
197   return 0;
198 }
199