2 * rs-serve - (c) 2013 Niklas E. Cathor
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.
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/>.
15 #define ASSERT(cond, desc) { \
17 perror(#desc " failed"); \
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)
25 unsigned int request_count;
27 static const char * method_strmap[] = {
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));
53 switch(siginfo.ssi_signo) {
59 log_error("Unhandled signal caught: %s", strsignal(siginfo.ssi_signo));
63 struct event_base *rs_event_base = NULL;
65 void log_event_base_message(int severity, const char *msg) {
66 char *format = "(from libevent) %s";
69 log_debug(format, msg);
72 log_info(format, msg);
75 log_error(format, msg);
78 log_warn(format, msg);
83 static evhtp_res finish_request(evhtp_request_t *req, void *arg) {
85 log_info("[rc=%d] %s %s -> %d (fini: %d)", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
89 static evhtp_res receive_path(evhtp_request_t *req, evhtp_path_t *path, void *arg) {
91 log_info("[rc=%d] (start) %s %s", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
93 char *username = path->match_start;
94 uid_t uid = user_get_uid(username);
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);
103 log_debug("User found: %s (uid: %ld)", username, uid);
106 log_error("%s:%d [possible bug] - request paused", __FILE__, __LINE__);
107 return EVHTP_RES_PAUSE;
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);
116 static void handle_storage(evhtp_request_t *req, void *arg) {
118 add_cors_headers(req);
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;
132 if(req->status == 0) {
133 switch(req->method) {
134 case htp_method_OPTIONS:
135 req->status = EVHTP_RES_NOCONTENT;
138 req->status = storage_handle_get(req);
140 case htp_method_HEAD:
141 req->status = storage_handle_head(req);
144 req->status = storage_handle_put(req);
146 case htp_method_DELETE:
147 req->status = storage_handle_delete(req);
150 req->status = EVHTP_RES_METHNALLOWED;
153 if(req->status != 0) {
154 evhtp_send_reply(req, req->status);
158 magic_t magic_cookie;
160 int main(int argc, char **argv) {
162 init_config(argc, argv);
164 open_authorizations("r");
168 /** OPEN MAGIC DATABASE **/
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));
176 log_info("starting process: main");
178 if(prctl(PR_SET_NAME, "rs-serve [main]", 0, 0, 0) != 0) {
179 log_error("Failed to set process name: %s", strerror(errno));
182 /** SETUP EVENT BASE **/
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);
189 // TODO: add error cb to base
192 /** SETUP LISTENER **/
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);
200 evhtp_t *server = evhtp_new(rs_event_base, NULL);
202 evhtp_set_cb(server, "/.well-known/webfinger", handle_webfinger, NULL);
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);
208 evhtp_callback_t *storage_cb = evhtp_set_regex_cb(server, "^/storage/([^/]+)/.*$", handle_storage, NULL);
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);
213 if(evhtp_bind_sockaddr(server, (struct sockaddr*)&sin, sizeof(sin), 1024) != 0) {
214 log_error("evhtp_bind_sockaddr() failed: %s", strerror(errno));
218 /** SETUP SIGNALS **/
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()");
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);
233 /** WRITE PID FILE **/
235 fprintf(RS_PID_FILE, "%d", getpid());
239 /** RUN EVENT LOOP **/
241 return event_base_dispatch(rs_event_base);