/* * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "Ecore_Con.h" #include "ecore_con_private.h" #define LENGTH_OF_SOCKADDR_UN(s) (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path)) #define LENGTH_OF_ABSTRACT_SOCKADDR_UN(s, path) (strlen(path) + 1 + (size_t)(((struct sockaddr_un *)NULL)->sun_path)) static int _ecore_con_local_init_count = 0; int ecore_con_local_init(void) { if (++_ecore_con_local_init_count != 1) return _ecore_con_local_init_count; return _ecore_con_local_init_count; } int ecore_con_local_shutdown(void) { if (--_ecore_con_local_init_count != 0) return _ecore_con_local_init_count; return _ecore_con_local_init_count; } int ecore_con_local_connect(Ecore_Con_Server *svr, int (*cb_done)(void *data, Ecore_Fd_Handler *fd_handler), void *data, void (*cb_free)(void *data, void *ev)) { char buf[4096]; struct sockaddr_un socket_unix; int curstate = 0; const char *homedir; int socket_unix_len; if (svr->type == ECORE_CON_LOCAL_USER) { homedir = getenv("HOME"); if (!homedir) homedir = getenv("TMP"); if (!homedir) homedir = "/tmp"; snprintf(buf, sizeof(buf), "%s/.ecore/%s/%i", homedir, svr->name, svr->port); } else if (svr->type == ECORE_CON_LOCAL_SYSTEM) { if (svr->port < 0) { if (svr->name[0] == '/') strncpy(buf, svr->name, sizeof(buf)); else snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s", svr->name); } else { if (svr->name[0] == '/') snprintf(buf, sizeof(buf), "%s|%i", svr->name, svr->port); else snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s|%i", svr->name, svr->port); } } else if (svr->type == ECORE_CON_LOCAL_ABSTRACT) strncpy(buf, svr->name, sizeof(buf)); svr->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (svr->fd < 0) return 0; if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) return 0; if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) return 0; if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) return 0; socket_unix.sun_family = AF_UNIX; if (svr->type == ECORE_CON_LOCAL_ABSTRACT) { #ifdef HAVE_ABSTRACT_SOCKETS /* copy name insto sun_path, prefixed by null to indicate abstract namespace */ snprintf(socket_unix.sun_path, sizeof(socket_unix.sun_path), ".%s", svr->name); socket_unix.sun_path[0] = '\0'; socket_unix_len = LENGTH_OF_ABSTRACT_SOCKADDR_UN(&socket_unix, svr->name); #else WRN("Your system does not support abstract sockets!"); return 0; #endif } else { strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path)); socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix); } if (connect(svr->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) return 0; svr->path = strdup(buf); if (!svr->path) return 0; if (svr->type & ECORE_CON_SSL) ecore_con_ssl_server_init(svr); svr->fd_handler = ecore_main_fd_handler_add(svr->fd, ECORE_FD_READ, cb_done, svr, NULL, NULL); if (!svr->fd_handler) return 0; if (!svr->delete_me) { /* we got our server! */ Ecore_Con_Event_Server_Add *e; e = calloc(1, sizeof(Ecore_Con_Event_Server_Add)); if (e) { svr->event_count++; e->server = svr; ecore_event_add(ECORE_CON_EVENT_SERVER_ADD, e, cb_free, NULL); } } return 1; } int ecore_con_local_listen(Ecore_Con_Server *svr, int (*cb_listen)(void *data, Ecore_Fd_Handler *fd_handler), void *data) { char buf[4096]; struct sockaddr_un socket_unix; struct linger lin; mode_t pmode; const char *homedir; struct stat st; mode_t mask; int socket_unix_len; mask = S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; if (svr->type == ECORE_CON_LOCAL_USER) { homedir = getenv("HOME"); if (!homedir) homedir = getenv("TMP"); if (!homedir) homedir = "/tmp"; mask = S_IRUSR | S_IWUSR | S_IXUSR; snprintf(buf, sizeof(buf), "%s/.ecore", homedir); if (stat(buf, &st) < 0) mkdir(buf, mask); snprintf(buf, sizeof(buf), "%s/.ecore/%s", homedir, svr->name); if (stat(buf, &st) < 0) mkdir(buf, mask); snprintf(buf, sizeof(buf), "%s/.ecore/%s/%i", homedir, svr->name, svr->port); mask = S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH; } else if (svr->type == ECORE_CON_LOCAL_SYSTEM) { mask = 0; if (svr->name[0] == '/') { if (svr->port >= 0) snprintf(buf, sizeof(buf), "%s|%i", svr->name, svr->port); else snprintf(buf, sizeof(buf), "%s", svr->name); } else snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s|%i", svr->name, svr->port); } else if (svr->type == ECORE_CON_LOCAL_ABSTRACT) strncpy(buf, svr->name, sizeof(buf)); pmode = umask(mask); start: svr->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (svr->fd < 0) goto error_umask; if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error_umask; if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error_umask; lin.l_onoff = 1; lin.l_linger = 0; if (setsockopt(svr->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0) goto error_umask; socket_unix.sun_family = AF_UNIX; if (svr->type == ECORE_CON_LOCAL_ABSTRACT) { #ifdef HAVE_ABSTRACT_SOCKETS /* . is a placeholder */ snprintf(socket_unix.sun_path, sizeof(socket_unix.sun_path), ".%s", svr->name); /* first char null indicates abstract namespace */ socket_unix.sun_path[0] = '\0'; socket_unix_len = LENGTH_OF_ABSTRACT_SOCKADDR_UN(&socket_unix, svr->name); #else ERR("Your system does not support abstract sockets!"); goto error_umask; #endif } else { strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path)); socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix); } if (bind(svr->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) { if (((svr->type == ECORE_CON_LOCAL_USER) || (svr->type == ECORE_CON_LOCAL_SYSTEM)) && (connect(svr->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) && (unlink(buf) >= 0)) goto start; else goto error_umask; } if (listen(svr->fd, 4096) < 0) goto error_umask; svr->path = strdup(buf); if (!svr->path) goto error_umask; svr->fd_handler = ecore_main_fd_handler_add(svr->fd, ECORE_FD_READ, cb_listen, svr, NULL, NULL); umask(pmode); if (!svr->fd_handler) goto error; return 1; error_umask: umask(pmode); error: return 0; }