1 /** 2 * Copyright (c) 2013 Larisa Grigore. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/shm.h> 28 #include <sys/stat.h> 29 30 #include <err.h> 31 #include <errno.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 #include <poll.h> 38 #include <sysexits.h> 39 #include <libutil.h> 40 41 #include "sysvipc_hash.h" 42 #include "sysvipc_sockets.h" 43 #include "utilsd.h" 44 #include "shmd.h" 45 46 #define MAX_CLIENTS 256 47 48 void usage(void) __dead2; 49 50 51 struct pollfd poll_fds[MAX_CLIENTS]; 52 struct client *clients[MAX_CLIENTS]; 53 int nr_poll_fds; 54 55 struct hashtable *clientshash = NULL; 56 57 int sysvd_debug; 58 int sysvd_daemon; 59 60 static int 61 remove_sysv_dir(void) 62 { 63 /* 64 * It is not necessary to check if the dir is empty and delete all files 65 * in it. Every time a client or the daemon exists all fds are closed 66 * and all resources are deleted (the daemon calls unlink after open a 67 * file for a sysv resource. 68 */ 69 return (rmdir(DIRPATH)); 70 } 71 72 static int 73 create_sysv_dir(void) 74 { 75 remove_sysv_dir(); 76 return (mkdir(DIRPATH, 0600)); 77 } 78 79 static int 80 daemon_init(void) 81 { 82 int error; 83 int socket_fd; 84 85 /* Create and init structures used for clients. */ 86 clientshash = _hash_init(MAX_CLIENTS); 87 if (!clientshash) 88 return (-1); 89 90 /* Create sysv resources directory. */ 91 error = create_sysv_dir(); 92 if (error) { 93 sysvd_print_err("You must first remove %s dir\n", 94 DIRPATH); 95 goto err; 96 } 97 98 /* Open socket used to receive connections. */ 99 unlink(LISTEN_SOCKET_FILE); 100 umask(0); 101 int fd_tmp = open(LISTEN_SOCKET_FILE, O_EXCL | O_CREAT, 0666); 102 if (fd_tmp < 0) { 103 sysvd_print_err("Could not open %s\n", LISTEN_SOCKET_FILE); 104 goto err; 105 } 106 close(fd_tmp); 107 108 socket_fd = init_socket(LISTEN_SOCKET_FILE); 109 if (socket_fd < 0) { 110 sysvd_print_err("Could not init %s socket\n", LISTEN_SOCKET_FILE); 111 goto err; 112 } 113 114 poll_fds[SOCKET_FD_IDX].fd = socket_fd; 115 poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI; 116 poll_fds[SOCKET_FD_IDX].revents = 0; 117 nr_poll_fds++; 118 119 shminit(); 120 121 return (0); 122 err: 123 free(clientshash); 124 return (-1); 125 } 126 127 static int 128 daemon_add_client(void) 129 { 130 struct client *cl; 131 //int on = 1; 132 struct cmsgcred cred; 133 char test; 134 135 cl = malloc(sizeof(*cl)); 136 if (!cl) { 137 sysvd_print_err("malloc"); 138 return (-1); 139 } 140 141 cl->undoid = -1; 142 143 /* Segments attached to a process. It is used 144 * when the process dies. 145 */ 146 LIST_INIT(&cl->ids_attached); 147 148 /* Init communication channel between daemon and client. */ 149 cl->sock = handle_new_connection(poll_fds[SOCKET_FD_IDX].fd); 150 151 poll_fds[nr_poll_fds].fd = cl->sock; 152 poll_fds[nr_poll_fds].events = POLLIN; 153 poll_fds[nr_poll_fds].revents = 0; 154 155 clients[nr_poll_fds] = cl; 156 nr_poll_fds++; 157 158 if(nr_poll_fds == MAX_CLIENTS) { 159 sysvd_print_err("No room for another client; connection refused\n"); 160 poll_fds[SOCKET_FD_IDX].events = 0; 161 } 162 163 /* Get the client pid. */ 164 receive_msg_with_cred(cl->sock, &test, sizeof(test), &cred); 165 cl->pid = cred.cmcred_pid; 166 167 sysvd_print("total = %d...another one will be added\n", nr_poll_fds); 168 sysvd_print("pid = %d connected\n", cl->pid); 169 170 /* Verify if the client is already connected using the hashtable. */ 171 if (_hash_lookup(clientshash, cl->pid)) { 172 errno = EEXIST; 173 sysvd_print_err("client already added"); 174 free(cl); 175 return (-1); 176 } 177 178 /* Insert client in hashtable. */ 179 _hash_insert(clientshash, cl->pid, cl); 180 181 return (0); 182 } 183 184 static void 185 daemon_remove_client(int i) 186 { 187 188 struct client *cl = clients[i]; 189 sysvd_print("pid %d disconected\n", cl->pid); 190 sysvd_print("total = %d\n", nr_poll_fds); 191 192 /* Close communication channels. */ 193 close(cl->sock); 194 195 /* Put last client on i position. */ 196 if (i != nr_poll_fds - 1) { 197 poll_fds[i] = poll_fds[nr_poll_fds - 1]; 198 clients[i] = clients[nr_poll_fds - 1]; 199 } 200 201 semexit(cl->undoid); 202 shmexit(cl); 203 204 _hash_remove(clientshash, cl->pid); 205 nr_poll_fds--; 206 free(cl); 207 cl = NULL; 208 209 if(nr_poll_fds == MAX_CLIENTS - 1) { 210 sysvd_print_err("Now another connexion can be handled\n"); 211 poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI; 212 } 213 } 214 215 static int 216 daemon_handle_msg(int i) 217 { 218 int msg_type; 219 struct shmget_msg shmget_msg; 220 struct shmctl_msg shmctl_msg; 221 struct shmat_msg shmat_msg; 222 int shmid; 223 int error; 224 struct cmsgcred cred; 225 226 int fd_send, fd_recv; 227 fd_send = fd_recv = clients[i]->sock; 228 229 msg_type = receive_type_message(fd_recv); 230 sysvd_print("type = %d from %d\n", msg_type, clients[i]->pid); 231 232 switch(msg_type) { 233 case CONNEXION_CLOSED: 234 sysvd_print("connection closed\n"); 235 return (EOF); 236 case SHMGET: 237 case SEMGET: 238 case MSGGET: 239 case UNDOGET: 240 receive_msg_with_cred(fd_recv, (char *)&shmget_msg, 241 sizeof(shmget_msg), &cred); 242 shmid = handle_shmget(clients[i]->pid, 243 &shmget_msg, &cred); 244 245 /* Send the shmid. */ 246 write(fd_send, (char *)&shmid, 247 sizeof(shmid)); 248 sysvd_print("sent %d to client %d\n", 249 shmid, clients[i]->pid); 250 break; 251 case SHMAT: 252 receive_msg_with_cred(fd_recv, (char *)&shmat_msg, 253 sizeof(shmat_msg), &cred); 254 error = handle_shmat(clients[i]->pid, 255 &shmat_msg, &cred); 256 257 /* Send the error after few checks. */ 258 write(fd_send, (char *)&error, 259 sizeof(error)); 260 break; 261 case SHMCTL: 262 receive_msg_with_cred(fd_recv, (char *)&shmctl_msg, 263 sizeof(shmctl_msg), &cred); 264 error = handle_shmctl(&shmctl_msg, &cred); 265 266 /* Send the error after few checks. */ 267 write(fd_send, (char *)&error, 268 sizeof(error)); 269 if (error == 0 && shmctl_msg.cmd == IPC_STAT) { 270 271 write(fd_send, (char *)&shmctl_msg.buf, 272 sizeof(struct shmid_ds)); 273 } 274 break; 275 case SHMDT: 276 receive_msg_with_cred(fd_recv, (char *)&shmid, 277 sizeof(shmid), NULL); 278 shmid = handle_shmdt(clients[i]->pid, shmid); 279 break; 280 default: 281 break; 282 } 283 sysvd_print("end\n"); 284 return (0); 285 } 286 287 288 static int 289 daemon_func(void) 290 { 291 int i; 292 //int msg; 293 int ret, r; 294 295 while(1) 296 { 297 ret = poll(poll_fds, nr_poll_fds, INFTIM); 298 if (ret < 0) { 299 sysvd_print_err("poll"); 300 return (-1); 301 } 302 for (i=0; (i < nr_poll_fds) && ret; i++) { 303 if (poll_fds[i].revents == 0) 304 continue; 305 ret--; 306 307 switch(i) { 308 case SOCKET_FD_IDX: 309 daemon_add_client(); 310 break; 311 default: 312 r = daemon_handle_msg(i); 313 if (r == EOF) { 314 daemon_remove_client(i); 315 i--; 316 } 317 break; 318 } 319 } 320 fflush(stdout); 321 } 322 323 return (0); 324 } 325 326 void 327 usage(void) 328 { 329 fprintf(stderr, "sysvipcd [-df] [-p pidfile]\n"); 330 exit(EX_USAGE); 331 } 332 333 int 334 main(int argc, char *argv[]) 335 { 336 int c; 337 int error; 338 char *pidfilename = NULL; 339 struct pidfh *pfh = NULL; 340 341 sysvd_debug = 0; 342 sysvd_daemon = 1; 343 344 while ((c = getopt(argc,argv,"dfp:")) !=-1) { 345 switch(c) { 346 case 'd': 347 sysvd_debug = 1; 348 sysvd_daemon = 0; 349 break; 350 case 'f': 351 sysvd_daemon = 0; 352 break; 353 case 'p': 354 pidfilename = optarg; 355 break; 356 default: 357 usage(); 358 break; 359 } 360 } 361 362 #ifdef SYSV_SEMS 363 sysvd_print("SYSV_SEMS defined (used for sysv sems); " 364 "a group of semaphores is protected)\n" 365 "by a rwlock and each semaphore is protected by a mutex\n"); 366 #else 367 sysvd_print("SYSV_SEMS not defined (used for sysv sems); " 368 "a group of semaphores is protected)\n" 369 "by a rwlock\n"); 370 #endif 371 372 sysvd_print("daemon starting\n"); 373 error = daemon_init(); 374 if (error) 375 goto out; 376 377 if (sysvd_daemon == 1) { 378 pfh = pidfile_open(pidfilename, 600, NULL); 379 daemon(1,0); 380 pidfile_write(pfh); 381 } 382 383 daemon_func(); 384 385 /* It won't reach here. */ 386 sysvd_print("daemon finished\n"); 387 388 //shmfree(); 389 remove_sysv_dir(); 390 out: 391 return (0); 392 } 393