beans.c (3886B)
1 /* beans is a simple pastebin server */ 2 3 #include <errno.h> 4 #include <netdb.h> 5 #include <stdarg.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/socket.h> 10 #include <sys/stat.h> 11 #include <sys/types.h> 12 #include <unistd.h> 13 #include <signal.h> 14 15 #include "arg.h" 16 17 #define BACKLOG 32 18 #define MAXSIZE 32000 /* in bytes */ 19 20 /* function declarations */ 21 void die(const char *errstr, ...); 22 int bindon(char *port); 23 void *ecalloc(size_t nmemb, size_t size); 24 char *readall(int sd, int *len, int limit); 25 void run(void); 26 void serve(int sd); 27 void sout(int sd, char *fmt, ...); 28 29 /* variables */ 30 char *argv0; 31 char port[8] = "2023"; 32 char base[256] = ""; 33 char path[256] = "/tmp"; 34 char mode[8] = "0600"; 35 int sockd; 36 37 /* function implementations */ 38 void 39 die(const char *errstr, ...) { 40 va_list ap; 41 42 va_start(ap, errstr); 43 vfprintf(stderr, errstr, ap); 44 va_end(ap); 45 exit(1); 46 } 47 48 int 49 bindon(char *port) { 50 struct addrinfo hints, *res; 51 int sd = 0, e; 52 53 memset(&hints, 0, sizeof hints); 54 hints.ai_family = AF_UNSPEC; 55 hints.ai_socktype = SOCK_STREAM; 56 hints.ai_flags = AI_PASSIVE; 57 58 if((e = getaddrinfo(NULL, port, &hints, &res))) 59 die("getaddrinfo(): %s\n", gai_strerror(e)); 60 if((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) 61 die("socket(): %s\n", strerror(errno)); 62 if(bind(sd, res->ai_addr, res->ai_addrlen) == -1) 63 die("bind(): %s\n", strerror(errno)); 64 if(listen(sd, BACKLOG) == -1) 65 die("listen(): %s\n", strerror(errno)); 66 return sd; 67 } 68 69 void * 70 ecalloc(size_t nmemb, size_t size) { 71 void *p; 72 73 if(!(p = calloc(nmemb, size))) 74 die("Cannot allocate memory.\n"); 75 return p; 76 } 77 78 char * 79 readall(int sd, int *len, int limit) { 80 char *buf; 81 int sz = 512, r, l = 0; 82 83 if(limit && sz > limit) 84 sz = limit; 85 86 buf = ecalloc(1, sz); 87 while((r = read(sd, &buf[l], sz - l)) != -1) { 88 if(!r) 89 break; 90 l += r; 91 if(limit && l == limit) 92 break; 93 if(l == sz) { 94 sz *= 2; 95 if(limit && sz > limit) 96 sz = limit; 97 if(!(buf = realloc(buf, sz))) 98 die("realloc()\n"); 99 } 100 } 101 if(r == -1) { 102 free(buf); 103 return NULL; 104 } 105 if(len) 106 *len = l; 107 buf[l] = '\0'; 108 return buf; 109 } 110 111 void 112 run(void) { 113 struct sockaddr_storage conn; 114 socklen_t size; 115 int csd; 116 pid_t pid; 117 118 while(1) { 119 size = sizeof conn; 120 csd = accept(sockd, (struct sockaddr *)&conn, &size); 121 if(csd == -1) { 122 fprintf(stderr, "accept(): %s", strerror(errno)); 123 continue; 124 } 125 pid = fork(); 126 if(pid) { 127 if(pid == -1) 128 fprintf(stderr, "fork(): %s\n", strerror(errno)); 129 close(csd); 130 continue; 131 } 132 serve(csd); 133 close(csd); 134 break; 135 } 136 } 137 138 void 139 serve(int sd) { 140 int len, tmpsd; 141 char *buf, *code; 142 char tmpfn[320] = {0}; 143 144 buf = readall(sd, &len, MAXSIZE); 145 if(!(buf && len)) { 146 sout(sd, "Nothing pasted.\n"); 147 return; 148 } 149 snprintf(tmpfn, sizeof tmpfn, "%s/beans.XXXXXX", path); 150 tmpsd = mkstemp(tmpfn); 151 if(tmpsd == -1) { 152 fprintf(stderr, "mkstemp()\n"); 153 free(buf); 154 return; 155 } 156 if(write(tmpsd, buf, len) == -1) 157 fprintf(stderr, "write(): %s\n", strerror(errno)); 158 code = strchr(tmpfn, '.')+1; 159 if(*base) 160 sout(sd, "%s%s\n", base, code); 161 else 162 sout(sd, "%s\n", code); 163 fchmod(tmpsd, strtol(mode, 0, 8)); 164 close(tmpsd); 165 free(buf); 166 } 167 168 void 169 sout(int sd, char *fmt, ...) { 170 va_list ap; 171 char buf[4096]; 172 int sz; 173 174 va_start(ap, fmt); 175 sz = vsnprintf(buf, sizeof buf, fmt, ap); 176 va_end(ap); 177 send(sd, buf, sz, 0); 178 } 179 180 int 181 main(int argc, char *argv[]) { 182 ARGBEGIN { 183 case 'b': strncpy(base, EARGF(die("%s: missing base URL\n", argv0)), sizeof base); break; 184 case 'd': strncpy(path, EARGF(die("%s: missing path\n", argv0)), sizeof path); break; 185 case 'm': strncpy(mode, EARGF(die("%s: missing mode\n", argv0)), sizeof mode); break; 186 case 'p': strncpy(port, EARGF(die("%s: missing port\n", argv0)), sizeof port); break; 187 case 'v': die("beans-"VERSION"\n"); 188 } ARGEND 189 190 sockd = bindon(port); 191 signal(SIGCHLD, SIG_IGN); /* cleanup zombies */ 192 run(); 193 close(sockd); 194 return 0; 195 }