general cleanup, clarify request lifecycle (pretty much all error responses end up...
[krsd] / src / process / main.c
1 /*
2  * rs-serve - (c) 2013 Niklas E. Cathor
3  *
4  * This program is distributed in the hope that it will be useful,
5  * but WITHOUT ANY WARRANTY; without even the implied warranty of
6  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
7  * GNU Affero General Public License for more details.
8  *
9  * You should have received a copy of the GNU Affero General Public License
10  * along with this program. If not, see <http://www.gnu.org/licenses/>.
11  */
12
13 #include "rs-serve.h"
14
15 #define ASSERT(cond, desc) {                    \
16     if(!(cond)) {                               \
17       perror(#desc " failed");                  \
18       exit(EXIT_FAILURE);                       \
19     }                                           \
20   }
21 #define ASSERT_NOT_NULL(var, desc) ASSERT((var) != NULL, desc)
22 #define ASSERT_ZERO(var, desc)     ASSERT((var) == 0, desc)
23 #define ASSERT_NOT_EQ(a, b, desc)  ASSERT((a) != (b), desc)
24
25 unsigned int request_count;
26
27 static const char * method_strmap[] = {
28     "GET",
29     "HEAD",
30     "POST",
31     "PUT",
32     "DELETE",
33     "MKCOL",
34     "COPY",
35     "MOVE",
36     "OPTIONS",
37     "PROPFIND",
38     "PROPATCH",
39     "LOCK",
40     "UNLOCK",
41     "TRACE",
42     "CONNECT",
43     "PATCH",
44 };
45
46 void handle_signal(evutil_socket_t fd, short events, void *arg) {
47   log_info("handle_signal()");
48   struct signalfd_siginfo siginfo;
49   if(read(fd, &siginfo, sizeof(siginfo)) < 0) {
50     log_error("Failed to read signal: %s", strerror(errno));
51     return;
52   }
53   switch(siginfo.ssi_signo) {
54   case SIGINT:
55   case SIGTERM:
56     exit(EXIT_SUCCESS);
57     break;
58   default:
59     log_error("Unhandled signal caught: %s", strsignal(siginfo.ssi_signo));
60   }
61 }
62
63 struct event_base *rs_event_base = NULL;
64
65 void log_event_base_message(int severity, const char *msg) {
66   char *format = "(from libevent) %s";
67   switch(severity) {
68   case EVENT_LOG_DEBUG:
69     log_debug(format, msg);
70     break;
71   case EVENT_LOG_MSG:
72     log_info(format, msg);
73     break;
74   case EVENT_LOG_ERR:
75     log_error(format, msg);
76     break;
77   case EVENT_LOG_WARN:
78     log_warn(format, msg);
79     break;
80   }
81 }
82
83 static evhtp_res finish_request(evhtp_request_t *req, void *arg) {
84   request_count--;
85   log_info("[rc=%d] %s %s -> %d (fini: %d)", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
86   return 0;
87 }
88
89 static evhtp_res receive_path(evhtp_request_t *req, evhtp_path_t *path, void *arg) {
90   request_count++;
91   log_info("[rc=%d] (start) %s %s", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
92
93   char *username = path->match_start;
94   uid_t uid = user_get_uid(username);
95   if(uid == -1) {
96     evhtp_send_reply(req, EVHTP_RES_NOTFOUND);
97   } else if(uid == -2) {
98     evhtp_send_reply(req, EVHTP_RES_SERVERR);
99   } else if(! UID_ALLOWED(uid)) {
100     log_info("User not allowed: %s (uid: %ld)", username, uid);
101     evhtp_send_reply(req, EVHTP_RES_NOTFOUND);
102   } else {
103     log_debug("User found: %s (uid: %ld)", username, uid);
104     return EVHTP_RES_OK;
105   }
106   log_error("%s:%d [possible bug] - request paused", __FILE__, __LINE__);
107   return EVHTP_RES_PAUSE;
108 }
109
110 void add_cors_headers(evhtp_request_t *req) {
111   ADD_RESP_HEADER(req, "Access-Control-Allow-Origin", RS_ALLOW_ORIGIN);
112   ADD_RESP_HEADER(req, "Access-Control-Allow-Headers", RS_ALLOW_HEADERS);
113   ADD_RESP_HEADER(req, "Access-Control-Allow-Methods", RS_ALLOW_METHODS);
114 }
115
116 static void handle_storage(evhtp_request_t *req, void *arg) {
117
118   add_cors_headers(req);
119
120   if(req->method != htp_method_OPTIONS) {
121     int auth_result = authorize_request(req);
122     if(auth_result == 0) {
123       log_debug("Request authorized.");
124     } else if(auth_result == -1) {
125       log_info("Request NOT authorized.");
126       req->status = EVHTP_RES_UNAUTH;
127     } else if(auth_result == -2) {
128       log_error("An error occured while authorizing request.");    
129       req->status = EVHTP_RES_SERVERR; 
130     }
131   }
132   if(req->status == 0) {
133     switch(req->method) {
134     case htp_method_OPTIONS:
135       req->status = EVHTP_RES_NOCONTENT;
136       break;
137     case htp_method_GET:
138       req->status = storage_handle_get(req);
139       break;
140     case htp_method_HEAD:
141       req->status = storage_handle_head(req);
142       break;
143     case htp_method_PUT:
144       req->status = storage_handle_put(req);
145       break;
146     case htp_method_DELETE:
147       req->status = storage_handle_delete(req);
148       break;
149     default:
150       req->status = EVHTP_RES_METHNALLOWED;
151     }
152   }
153   if(req->status != 0) {
154     evhtp_send_reply(req, req->status);
155   }
156 }
157
158 magic_t magic_cookie;
159
160 int main(int argc, char **argv) {
161
162   init_config(argc, argv);
163
164   open_authorizations("r");
165
166   init_webfinger();
167
168   /** OPEN MAGIC DATABASE **/
169
170   magic_cookie = magic_open(MAGIC_MIME);
171   if(magic_load(magic_cookie, RS_MAGIC_DATABASE) != 0) {
172     log_error("Failed to load magic database: %s", magic_error(magic_cookie));
173     exit(EXIT_FAILURE);
174   }
175
176   log_info("starting process: main");
177
178   if(prctl(PR_SET_NAME, "rs-serve [main]", 0, 0, 0) != 0) {
179     log_error("Failed to set process name: %s", strerror(errno));
180   }
181
182   /** SETUP EVENT BASE **/
183
184   rs_event_base = event_base_new();
185   ASSERT_NOT_NULL(rs_event_base, "event_base_new()");
186   log_debug("libevent method: %s", event_base_get_method(rs_event_base));
187   event_set_log_callback(log_event_base_message);
188
189   // TODO: add error cb to base
190
191
192   /** SETUP LISTENER **/
193
194   struct sockaddr_in sin;
195   memset(&sin, 0, sizeof(struct sockaddr_in));
196   sin.sin_family = AF_INET;
197   sin.sin_addr.s_addr = htonl(0);
198   sin.sin_port = htons(RS_PORT);
199
200   evhtp_t *server = evhtp_new(rs_event_base, NULL);
201
202   evhtp_set_cb(server, "/.well-known/webfinger", handle_webfinger, NULL);
203
204   // support legacy webfinger clients (we don't support XRD though):
205   evhtp_set_cb(server, "/.well-known/host-meta", handle_webfinger, NULL);
206   evhtp_set_cb(server, "/.well-known/host-meta.json", handle_webfinger, NULL);
207
208   evhtp_callback_t *storage_cb = evhtp_set_regex_cb(server, "^/storage/([^/]+)/.*$", handle_storage, NULL);
209
210   evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_path, receive_path, NULL);
211   evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_request_fini, finish_request, NULL);
212
213   if(evhtp_bind_sockaddr(server, (struct sockaddr*)&sin, sizeof(sin), 1024) != 0) {
214     log_error("evhtp_bind_sockaddr() failed: %s", strerror(errno));
215     exit(EXIT_FAILURE);
216   }
217
218   /** SETUP SIGNALS **/
219
220   sigset_t sigmask;
221   sigemptyset(&sigmask);
222   sigaddset(&sigmask, SIGINT);
223   sigaddset(&sigmask, SIGTERM);
224   sigaddset(&sigmask, SIGCHLD);
225   ASSERT_ZERO(sigprocmask(SIG_BLOCK, &sigmask, NULL), "sigprocmask()");
226   int sfd = signalfd(-1, &sigmask, SFD_NONBLOCK);
227   ASSERT_NOT_EQ(sfd, -1, "signalfd()");
228
229   struct event *signal_event = event_new(rs_event_base, sfd, EV_READ | EV_PERSIST,
230                                          handle_signal, NULL);
231   event_add(signal_event, NULL);
232
233   /** WRITE PID FILE **/
234   if(RS_PID_FILE) {
235     fprintf(RS_PID_FILE, "%d", getpid());
236     fflush(RS_PID_FILE);
237   }
238
239   /** RUN EVENT LOOP **/
240
241   return event_base_dispatch(rs_event_base);
242 }