c1e19d904df51b23e3d88a81c4a19ac2e7d1cb75
[reposerve] / server / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <time.h>
7 #include <signal.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/select.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include "repo.h"
16 #include "client.h"
17
18 static const char *guess_repo_name(const char *path);
19 static void sighandler(int s);
20 static int parse_args(int argc, char **argv);
21
22 static const char *repo_name;
23 static int lis = -1;
24 static int port = 64357;
25 static int quit;
26
27 static struct client *clients;
28
29 const char *repo_path;
30
31
32 int main(int argc, char **argv)
33 {
34         struct sockaddr_in addr;
35
36         if(parse_args(argc, argv) == -1) {
37                 return 1;
38         }
39
40         signal(SIGINT, sighandler);
41         signal(SIGQUIT, sighandler);
42         signal(SIGILL, sighandler);
43         signal(SIGSEGV, sighandler);
44         signal(SIGTERM, sighandler);
45
46         if(repo_init(repo_path) == -1) {
47                 return 1;
48         }
49         /* repo_cleanup(); */
50
51         if(!repo_name) {
52                 if(!(repo_name = guess_repo_name(repo_path))) {
53                         return 1;
54                 }
55         }
56
57         printf("Serving %s from %s\n", repo_name, repo_path);
58
59         if(!(lis = socket(PF_INET, SOCK_STREAM, 0))) {
60                 fprintf(stderr, "failed to create socket: %s\n", strerror(errno));
61                 return 1;
62         }
63         fcntl(lis, F_SETFL, fcntl(lis, F_GETFL) | O_NONBLOCK);
64
65         memset(&addr, 0, sizeof addr);
66         addr.sin_family = AF_INET;
67         addr.sin_port = htons(port);
68         addr.sin_addr.s_addr = INADDR_ANY;
69
70         if(bind(lis, (struct sockaddr*)&addr, sizeof addr) == -1) {
71                 fprintf(stderr, "failed to bind listening socket to port %d\n", port);
72                 return 1;
73         }
74         listen(lis, 8);
75
76         for(;;) {
77                 int res, max_fd, s;
78                 fd_set rdset;
79                 struct client *c, dummy;
80
81                 FD_SET(lis, &rdset);
82                 max_fd = lis;
83
84                 c = clients;
85                 while(c) {
86                         FD_SET(c->s, &rdset);
87                         if(c->s > max_fd) max_fd = c->s;
88                         c = c->next;
89                 }
90
91                 if((res = select(max_fd + 1, &rdset, 0, 0, 0)) == -1 && errno != EINTR) {
92                         perror("select failed");
93                         break;
94                 }
95                 if(quit) break;
96                 if(res <= 0) continue;
97
98                 dummy.next = clients;
99                 c = &dummy;
100                 while(c->next) {
101                         if(FD_ISSET(c->next->s, &rdset)) {
102                                 if(handle_client(c->next) == -1) {
103                                         struct client *tmp = c->next;
104                                         c->next = tmp->next;
105
106                                         printf("Client %s disconnected\n", tmp->addr ? tmp->addr : "unknown");
107                                         close(tmp->s);
108                                         free(tmp->addr);
109                                         free(tmp);
110                                         continue;
111                                 }
112                         }
113                         c = c->next;
114                 }
115                 clients = dummy.next;
116
117                 if(FD_ISSET(lis, &rdset)) {
118                         struct sockaddr_in addr;
119                         unsigned int addrsz = sizeof addr;
120
121                         if((s = accept(lis, (struct sockaddr*)&addr, &addrsz)) == -1) {
122                                 perror("failed to accept connection");
123                                 continue;
124                         }
125                         fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
126
127                         if(!(c = calloc(1, sizeof *c))) {
128                                 perror("failed to allocate memory for client structure");
129                                 close(s);
130                                 continue;
131                         }
132                         c->s = s;
133                         c->next = clients;
134                         c->addr = strdup(inet_ntoa(addr.sin_addr));
135                         clients = c;
136
137                         send_string(c, "reposerve-0.1 protocol:0\n");
138
139                         printf("Connection from: %s\n", c->addr ? c->addr : "unknown");
140                 }
141         }
142
143         printf("Shutting down\n");
144         return 0;
145 }
146
147 static const char *guess_repo_name(const char *path)
148 {
149         static char pathbuf[PATH_MAX];
150         char *last_slash;
151         int len;
152
153         if(!realpath(path, pathbuf)) {
154                 goto fail;
155         }
156         len = strlen(pathbuf);
157
158         if(len > 0 && pathbuf[len - 1] == '/') {
159                 pathbuf[--len] = 0;
160         }
161         if(len <= 0) goto fail;
162
163         if((last_slash = strrchr(pathbuf, '/'))) {
164                 return last_slash + 1;
165         }
166         return pathbuf;
167
168 fail:
169         fprintf(stderr, "failed to resolve path %s while determining the repo name. Please specify an explicit name with -id\n", repo_path);
170         return 0;
171 }
172
173 static void sighandler(int s)
174 {
175         quit = 1;
176 }
177
178 static void print_usage(const char *argv0)
179 {
180         printf("Usage: %s [options] [repo path]\n", argv0);
181         printf("options:\n");
182         printf("  -id <name>: repo identifier (defaults to directory name)\n");
183         printf("  -h,-help: print help and exit\n");
184 }
185
186 static int parse_args(int argc, char **argv)
187 {
188         int i;
189
190         for(i=1; i<argc; i++) {
191                 if(argv[i][0] == '-') {
192                         if(strcmp(argv[i], "-id") == 0) {
193                                 repo_name = argv[++i];
194                         } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
195                                 print_usage(argv[0]);
196                                 exit(0);
197                         } else {
198                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
199                                 return -1;
200                         }
201                 } else {
202                         if(repo_path) {
203                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
204                                 return -1;
205                         }
206                         repo_path = argv[i];
207                 }
208         }
209
210         if(!repo_path) repo_path = ".";
211         return 0;
212 }