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