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 static const char * method_strmap[] = {
44 void handle_signal(evutil_socket_t fd, short events, void *arg) {
45 log_info("handle_signal()");
46 struct signalfd_siginfo siginfo;
47 if(read(fd, &siginfo, sizeof(siginfo)) < 0) {
48 log_error("Failed to read signal: %s", strerror(errno));
51 switch(siginfo.ssi_signo) {
57 log_error("Unhandled signal caught: %s", strsignal(siginfo.ssi_signo));
61 struct event_base *rs_event_base = NULL;
63 void log_event_base_message(int severity, const char *msg) {
64 char *format = "(from libevent) %s";
67 log_debug(format, msg);
70 log_info(format, msg);
73 log_error(format, msg);
76 log_warn(format, msg);
81 static evhtp_res finish_request(evhtp_request_t *req, void *arg) {
82 log_info("%s %s -> %d (fini: %d)", method_strmap[req->method], req->uri->path->full, req->status, req->finished);
86 static evhtp_res receive_path(evhtp_request_t *req, evhtp_path_t *path, void *arg) {
87 log_info("full path: %s (user: %s, path: %s, file: %s)",
88 path->full, path->match_start, path->match_end, path->file);
89 char *username = path->match_start;
90 uid_t uid = user_get_uid(username);
92 evhtp_send_reply(req, EVHTP_RES_NOTFOUND);
93 } else if(uid == -2) {
94 evhtp_send_reply(req, EVHTP_RES_SERVERR);
95 } else if(uid < RS_MIN_UID) {
96 log_info("User not allowed: %s (uid: %ld)", username, uid);
97 evhtp_send_reply(req, EVHTP_RES_NOTFOUND);
99 log_debug("User found: %s (uid: %ld)", username, uid);
102 return EVHTP_RES_PAUSE;
105 void add_cors_headers(evhtp_request_t *req) {
106 ADD_RESP_HEADER(req, "Access-Control-Allow-Origin", RS_ALLOW_ORIGIN);
107 ADD_RESP_HEADER(req, "Access-Control-Allow-Headers", RS_ALLOW_HEADERS);
108 ADD_RESP_HEADER(req, "Access-Control-Allow-Methods", RS_ALLOW_METHODS);
111 static evhtp_res handle_options(evhtp_request_t *req) {
112 evhtp_send_reply(req, EVHTP_RES_NOCONTENT);
113 return EVHTP_RES_PAUSE;
116 static evhtp_res receive_headers(evhtp_request_t *req, evhtp_headers_t *hdr, void *arg) {
118 add_cors_headers(req);
120 if(req->method == htp_method_OPTIONS) {
121 return handle_options(req);
124 int auth_result = authorize_request(req);
125 if(auth_result == 0) {
126 log_debug("Request authorized.");
128 } else if(auth_result == -1) {
129 log_info("Request NOT authorized.");
130 return EVHTP_RES_UNAUTH;
131 } else if(auth_result == -2) {
132 log_error("An error occured while authorizing request.");
133 return EVHTP_RES_SERVERR;
135 return EVHTP_RES_PAUSE;
138 static void handle_storage(evhtp_request_t *req, void *arg) {
139 switch(req->method) {
141 req->status = storage_handle_get(req);
143 case htp_method_HEAD:
144 req->status = storage_handle_head(req);
147 req->status = storage_handle_put(req);
149 //case htp_method_DELETE:
150 // req->status = storage_handle_delete(req);
153 req->status = EVHTP_RES_METHNALLOWED;
155 if(req->status != 0) {
156 evhtp_send_reply(req, req->status);
160 magic_t magic_cookie;
162 int main(int argc, char **argv) {
164 init_config(argc, argv);
166 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_callback_t *storage_cb = evhtp_set_regex_cb(server, "^/storage/([^/]+)/.*$", handle_storage, NULL);
204 evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_headers, receive_headers, NULL);
205 evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_path, receive_path, NULL);
206 evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_request_fini, finish_request, NULL);
208 if(evhtp_bind_sockaddr(server, (struct sockaddr*)&sin, sizeof(sin), 1024) != 0) {
209 log_error("evhtp_bind_sockaddr() failed: %s", strerror(errno));
213 /** SETUP SIGNALS **/
216 sigemptyset(&sigmask);
217 sigaddset(&sigmask, SIGINT);
218 sigaddset(&sigmask, SIGTERM);
219 sigaddset(&sigmask, SIGCHLD);
220 ASSERT_ZERO(sigprocmask(SIG_BLOCK, &sigmask, NULL), "sigprocmask()");
221 int sfd = signalfd(-1, &sigmask, SFD_NONBLOCK);
222 ASSERT_NOT_EQ(sfd, -1, "signalfd()");
224 struct event *signal_event = event_new(rs_event_base, sfd, EV_READ | EV_PERSIST,
225 handle_signal, NULL);
226 event_add(signal_event, NULL);
228 /** WRITE PID FILE **/
230 fprintf(RS_PID_FILE, "%d", getpid());
234 /** RUN EVENT LOOP **/
236 return event_base_dispatch(rs_event_base);