/** Event handler. Broadcasts updates to all clients. */ static void broadcast(fish_message_type_t type, const wchar_t *key, const wchar_t *val) { message_t *msg; if (connections.empty()) return; msg = create_message(type, key, val); /* Don't merge these loops, or try_send_all can free the message prematurely */ for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { msg->count++; iter->unsent.push(msg); } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { try_send_all(&*iter); } }
/** Event handler. Broadcasts updates to all clients. */ static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar_t *val ) { connection_t *c; message_t *msg; if( !conn ) return; msg = create_message( type, key, val ); /* Don't merge these loops, or try_send_all can free the message prematurely */ for( c = conn; c; c=c->next ) { msg->count++; c->unsent->push(msg); } for( c = conn; c; c=c->next ) { try_send_all( c ); } }
void enqueue_all(connection_t *c) { env_var_table_t::const_iterator iter; for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) { const wcstring &key = iter->first; const var_uni_entry_t *val = iter->second; message_t *msg = create_message(val->exportv?SET_EXPORT:SET, key.c_str(), val->val.c_str()); msg->count=1; c->unsent->push(msg); } try_send_all(c); }
void env_universal_destroy() { /* Go into blocking mode and send all data before exiting */ if (env_universal_server.fd >= 0) { if (fcntl(env_universal_server.fd, F_SETFL, 0) != 0) { wperror(L"fcntl"); } try_send_all(&env_universal_server); } connection_destroy(&env_universal_server); env_universal_server.fd =-1; s_env_univeral_inited = false; }
/** Main function for fishd */ int main( int argc, char ** argv ) { int child_socket; struct sockaddr_un remote; socklen_t t; int max_fd; int update_count=0; fd_set read_fd, write_fd; set_main_thread(); setup_fork_guards(); program_name=L"fishd"; wsetlocale( LC_ALL, L"" ); /* Parse options */ while( 1 ) { static struct option long_options[] = { { "help", no_argument, 0, 'h' } , { "version", no_argument, 0, 'v' } , { 0, 0, 0, 0 } } ; int opt_index = 0; int opt = getopt_long( argc, argv, GETOPT_STRING, long_options, &opt_index ); if( opt == -1 ) break; switch( opt ) { case 0: break; case 'h': print_help( argv[0], 1 ); exit(0); case 'v': debug( 0, L"%ls, version %s\n", program_name, PACKAGE_VERSION ); exit( 0 ); case '?': return 1; } } init(); while(1) { connection_t *c; int res; t = sizeof( remote ); FD_ZERO( &read_fd ); FD_ZERO( &write_fd ); FD_SET( sock, &read_fd ); max_fd = sock+1; for( c=conn; c; c=c->next ) { FD_SET( c->fd, &read_fd ); max_fd = maxi( max_fd, c->fd+1); if( ! c->unsent->empty() ) { FD_SET( c->fd, &write_fd ); } } while( 1 ) { res=select( max_fd, &read_fd, &write_fd, 0, 0 ); if( quit ) { save(); exit(0); } if( res != -1 ) break; if( errno != EINTR ) { wperror( L"select" ); exit(1); } } if( FD_ISSET( sock, &read_fd ) ) { if( (child_socket = accept( sock, (struct sockaddr *)&remote, &t) ) == -1) { wperror( L"accept" ); exit(1); } else { debug( 4, L"Connected with new child on fd %d", child_socket ); if( fcntl( child_socket, F_SETFL, O_NONBLOCK ) != 0 ) { wperror( L"fcntl" ); close( child_socket ); } else { connection_t *newc = (connection_t *)malloc( sizeof(connection_t)); connection_init( newc, child_socket ); newc->next = conn; send( newc->fd, GREETING, strlen(GREETING), MSG_DONTWAIT ); enqueue_all( newc ); conn=newc; } } } for( c=conn; c; c=c->next ) { if( FD_ISSET( c->fd, &write_fd ) ) { try_send_all( c ); } } for( c=conn; c; c=c->next ) { if( FD_ISSET( c->fd, &read_fd ) ) { read_message( c ); /* Occasionally we save during normal use, so that we won't lose everything on a system crash */ update_count++; if( update_count >= 64 ) { save(); update_count = 0; } } } connection_t *prev=0; c=conn; while( c ) { if( c->killme ) { debug( 4, L"Close connection %d", c->fd ); while( ! c->unsent->empty() ) { message_t *msg = c->unsent->front(); c->unsent->pop(); msg->count--; if( !msg->count ) free( msg ); } connection_destroy( c ); if( prev ) { prev->next=c->next; } else { conn=c->next; } free(c); c=(prev?prev->next:conn); } else { prev=c; c=c->next; } } if( !conn ) { debug( 0, L"No more clients. Quitting" ); save(); env_universal_common_destroy(); break; } } }
void env_universal_barrier() { ASSERT_IS_MAIN_THREAD(); message_t *msg; fd_set fds; if( !init || is_dead() ) return; barrier_reply = 0; /* Create barrier request */ msg= create_message( BARRIER, 0, 0); msg->count=1; env_universal_server.unsent->push(msg); /* Wait until barrier request has been sent */ debug( 3, L"Create barrier" ); while( 1 ) { try_send_all( &env_universal_server ); check_connection(); if( env_universal_server.unsent->empty() ) break; if( env_universal_server.fd == -1 ) { reconnect(); debug( 2, L"barrier interrupted, exiting" ); return; } FD_ZERO( &fds ); FD_SET( env_universal_server.fd, &fds ); select( env_universal_server.fd+1, 0, &fds, 0, 0 ); } /* Wait for barrier reply */ debug( 3, L"Sent barrier request" ); while( !barrier_reply ) { if( env_universal_server.fd == -1 ) { reconnect(); debug( 2, L"barrier interrupted, exiting (2)" ); return; } FD_ZERO( &fds ); FD_SET( env_universal_server.fd, &fds ); select( env_universal_server.fd+1, &fds, 0, 0, 0 ); env_universal_read_all(); } debug( 3, L"End barrier" ); }
/** Parse message msg */ static void parse_message(wchar_t *msg, connection_t *src) { // debug( 3, L"parse_message( %ls );", msg ); if (msg[0] == L'#') return; if (match(msg, SET_STR) || match(msg, SET_EXPORT_STR)) { wchar_t *name, *tmp; int exportv = match(msg, SET_EXPORT_STR); name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR)); while (wcschr(L"\t ", *name)) name++; tmp = wcschr(name, L':'); if (tmp) { wchar_t *key; wchar_t *val; key = (wchar_t *)malloc(sizeof(wchar_t)*(tmp-name+1)); memcpy(key, name, sizeof(wchar_t)*(tmp-name)); key[tmp-name]=0; val = tmp+1; val = unescape(val, 0); if (key && val) env_universal_common_set(key, val, exportv); free(val); free(key); } else { debug(1, PARSE_ERR, msg); } } else if (match(msg, ERASE_STR)) { wchar_t *name, *tmp; name = msg+wcslen(ERASE_STR); while (wcschr(L"\t ", *name)) name++; tmp = name; while (iswalnum(*tmp) || *tmp == L'_') tmp++; *tmp = 0; if (!wcslen(name)) { debug(1, PARSE_ERR, msg); } env_universal_common_remove(name); if (callback) { callback(ERASE, name, 0); } } else if (match(msg, BARRIER_STR)) { message_t *msg = create_message(BARRIER_REPLY, 0, 0); msg->count = 1; src->unsent->push(msg); try_send_all(src); } else if (match(msg, BARRIER_REPLY_STR)) { if (callback) { callback(BARRIER_REPLY, 0, 0); } } else { debug(1, PARSE_ERR, msg); } }
/** Main function for fishd */ int main(int argc, char ** argv) { int child_socket; struct sockaddr_un remote; socklen_t t; uid_t sock_euid; gid_t sock_egid; int max_fd; int update_count=0; fd_set read_fd, write_fd; set_main_thread(); setup_fork_guards(); program_name=L"fishd"; wsetlocale(LC_ALL, L""); /* Parse options */ while (1) { static struct option long_options[] = { { "help", no_argument, 0, 'h' } , { "version", no_argument, 0, 'v' } , { 0, 0, 0, 0 } } ; int opt_index = 0; int opt = getopt_long(argc, argv, GETOPT_STRING, long_options, &opt_index); if (opt == -1) break; switch (opt) { case 0: break; case 'h': print_help(argv[0], 1); exit(0); case 'v': debug(0, L"%ls, version %s\n", program_name, FISH_BUILD_VERSION); exit(0); case '?': return 1; } } init(); while (1) { int res; t = sizeof(remote); FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_SET(sock, &read_fd); max_fd = sock+1; for (connection_list_t::const_iterator iter = connections.begin(); iter != connections.end(); ++iter) { const connection_t &c = *iter; FD_SET(c.fd, &read_fd); max_fd = maxi(max_fd, c.fd+1); if (! c.unsent.empty()) { FD_SET(c.fd, &write_fd); } } while (1) { res=select(max_fd, &read_fd, &write_fd, 0, 0); if (quit) { save(); exit(0); } if (res != -1) break; if (errno != EINTR) { wperror(L"select"); exit(1); } } if (FD_ISSET(sock, &read_fd)) { if ((child_socket = accept(sock, (struct sockaddr *)&remote, &t)) == -1) { wperror(L"accept"); exit(1); } else { debug(4, L"Connected with new child on fd %d", child_socket); if (((getpeereid(child_socket, &sock_euid, &sock_egid) != 0) || sock_euid != geteuid())) { debug(1, L"Wrong credentials for child on fd %d", child_socket); close(child_socket); } else if (make_fd_nonblocking(child_socket) != 0) { wperror(L"fcntl"); close(child_socket); } else { connections.push_front(connection_t(child_socket)); connection_t &newc = connections.front(); send(newc.fd, GREETING, strlen(GREETING), MSG_DONTWAIT); enqueue_all(&newc); } } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { if (FD_ISSET(iter->fd, &write_fd)) { try_send_all(&*iter); } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end(); ++iter) { if (FD_ISSET(iter->fd, &read_fd)) { read_message(&*iter); /* Occasionally we save during normal use, so that we won't lose everything on a system crash */ update_count++; if (update_count >= 64) { save(); update_count = 0; } } } for (connection_list_t::iterator iter = connections.begin(); iter != connections.end();) { if (iter->killme) { debug(4, L"Close connection %d", iter->fd); while (! iter->unsent.empty()) { message_t *msg = iter->unsent.front(); iter->unsent.pop(); msg->count--; if (! msg->count) free(msg); } connection_destroy(&*iter); iter = connections.erase(iter); } else { ++iter; } } if (connections.empty()) { debug(0, L"No more clients. Quitting"); save(); break; } } }