static int dispatch_pictaker( const char *cmdbuf, const char *picture_mem ){ int command; if( sscanf( cmdbuf, "%d", &command ) != 1 ){ camserv_log( MODNAME, "Parse error reading data from pictaker!"); return -1; } if( command == 0 ) { char *new_picture_data; int nbytes; sscanf( cmdbuf, "%*d %d", &nbytes ); if( (new_picture_data = malloc( nbytes )) == NULL ){ camserv_log( MODNAME, "Error allocating memory to manage picture!"); return -1; } memcpy( new_picture_data, picture_mem, nbytes ); if( manager_new_picture( new_picture_data, nbytes, 100 ) == -1 ){ camserv_log( MODNAME, "Unable to manage picture!"); free( new_picture_data ); return -1; } } else { camserv_log( MODNAME, "Unknown pictaker dispatch command: %d", command ); return -1; } return 0; }
static ClientInfo *accept_client( const Socket *listen_socket ){ Socket *new_sock; ClientInfo *res; if( (new_sock = socket_accept( listen_socket )) == NULL ){ camserv_log( MODNAME, "Could not accept new client socket: %s", strerror( errno )); return NULL; } camserv_log( MODNAME, "Accepted new socket: %s", socket_query_remote_name( new_sock )); if( fcntl( socket_query_fd( new_sock ), F_SETFL, O_NONBLOCK ) == -1){ camserv_log( MODNAME, "Unable to set socket to nonblocking mode!"); socket_dest( new_sock ); return NULL; } if( (res = clientinfo_new( new_sock )) == NULL ){ camserv_log( MODNAME, "Error creating clientinfo structure!"); socket_dest( new_sock ); return NULL; } return res; }
static void sighandler( int signum ){ camserv_log( MODNAME, "Received signal: %d", signum ); if( signum == SIGPIPE ) return; camserv_log( MODNAME, "Aborting!"); Abort = 1; }
static int write_proxy_client( ClientInfo *cinfo, SockSet *writeset ){ int bufwrite; bufwrite = databuf_write( cinfo->writebuf, socket_query_fd( cinfo->socket )); if( bufwrite == -1 ) return -1; if( bufwrite == 1 ) return 0; /* In this case, we are done sending whatever it was that 'state' referred to */ if( cinfo->state == CINFO_STATE_SENDSIZE ) { databuf_buf_set( cinfo->writebuf, cinfo->proxypic_data, ntohl( cinfo->proxypic_size )); cinfo->state = CINFO_STATE_PICTURE; } else { /* Just finished sending a picture */ char *pic_data; size_t pic_size; int pic_id; /* Remove old info */ if( cinfo->management_data != NULL ) { if( manager_dest_client( cinfo->management_data ) == -1 ){ camserv_log( MODNAME, "Error destroying client management!"); } } cinfo->management_data = manager_new_client(&pic_data, &pic_size, &pic_id); if( cinfo->management_data == NULL ){ camserv_log( MODNAME, "Error managing proxy! (Picture may not be " "taken yet)"); sockset_hold( writeset, cinfo->socket ); return 0; } if( pic_id == cinfo->last_picture_id ) { manager_dest_client( cinfo->management_data ); cinfo->management_data = NULL; /* Proxy sucking too fast */ sockset_hold( writeset, cinfo->socket ); return 0; } camserv_log( MODNAME, "Writing picsize %d to client", (int) pic_size ); cinfo->proxypic_data = pic_data; cinfo->proxypic_size = htonl( pic_size ); cinfo->last_picture_id = pic_id; databuf_buf_set( cinfo->writebuf, &cinfo->proxypic_size, sizeof( cinfo->proxypic_size )); cinfo->state = CINFO_STATE_SENDSIZE; } return 0; }
static CamConfigSection *camconfig_add_section( CamConfig *ccfg, char *newsec ) { CamConfigSection *res; char *keyval; hnode_t *node; if( hash_lookup( ccfg->mainhash, newsec ) != NULL ){ camserv_log( "camconfig", "Section \"%s\" multi-defined in cfg", newsec ); return NULL; } if( (res = section_new( newsec )) == NULL ) return NULL; if( (keyval = strdup( newsec )) == NULL ){ section_dest( res ); return NULL; } if( (node = hnode_create( res )) == NULL ){ section_dest( res ); free( keyval ); return NULL; } hash_insert( ccfg->mainhash, node, keyval ); return res; }
void *filter_init( CamConfig *ccfg, char *section_name ){ JbmFilter *res; if( (res = malloc( sizeof( *res ))) == NULL ){ camserv_log( MODNAME, "FATAL! Could not allocate space for random filter!" ); return NULL; } return res; }
int camconfig_query_def_int( CamConfig *ccfg, char *secname, char *key,int def) { int err, res; res = camconfig_query_int( ccfg, secname, key, &err ); if( err == 1 ){ camserv_log( "camconfig", "Using default of \"%d\" for [%s]:%s", def, secname, key ); return def; } else return res; }
float camconfig_query_def_float( CamConfig *ccfg, char *secname, char *key, float def ) { hnode_t *node; CamConfigSection *section; if( (node = hash_lookup( ccfg->mainhash, secname )) == NULL ){ camserv_log( "camconfig", "Using default of \"%f\" for [%s]:%s", def, secname, key ); return def; } section = hnode_get( node ); if( (node = hash_lookup ( section->entryhash, key )) == NULL ){ camserv_log( "camconfig", "Using default of \"%f\" for [%s]:%s", def, secname, key ); return def; } return atof( hnode_get( node )); }
char *video_query_active_section( CamConfig *ccfg, char *place, int size){ const char *val; val = camconfig_query_str( ccfg, SEC_VIDEO, "video_section" ); if( val == NULL ){ camserv_log( MODNAME, "[%s]:video_section has not been set!", SEC_VIDEO ); return NULL; } strncpy( place, val, size - 1 ); place[ size - 1 ] = '\0'; return place; }
const int snap_single(CamConfig *ccfg ){ const char *snapfname; int presnaps; if( !(snapfname = camconfig_query_str( ccfg, SEC_MAIN, "output_snapfile"))) return 0; presnaps = camconfig_query_def_int( ccfg, SEC_MAIN, "output_presnaps", 0 ); if( picture_single( ccfg, snapfname, presnaps ) == -1 ){ camserv_log( "snap_single", "Failed to snap picture!" ); } return 1; }
static int process_color( const char *color, int is_bg, char *secname, unsigned char *rint, unsigned char *gint, unsigned char *bint ) { if( !color || !strlen( color )){ camserv_log( MODNAME, "Invalid [%s]:%s color given. " "Using default", secname, is_bg ? "bg" : "fg" ); if( is_bg ) { *rint = (0x00ff0000 & COLOR_DEFAULT_BG) >> 16; *gint = (0x0000ff00 & COLOR_DEFAULT_BG) >> 8; *bint = (0x000000ff & COLOR_DEFAULT_BG) >> 0; return COLOR_DEFAULT_BG; } else {
CamConfig *camconfig_read( FILE *fp ){ CamConfigSection *current_section; CamConfig *ccfg; char buf[ 1024 ], *cp, *endcp, key[ 1024 ], value[ 1024 ]; int lineno; if( (ccfg = camconfig_new()) == NULL ){ camserv_log( "camconfig", "Error allocating memory for config!"); return NULL; } current_section = NULL; lineno = 0; while( fgets( buf, sizeof( buf ), fp ) != NULL ){ lineno++; if( buf[ 0 ] == '#' || buf[ 0 ] == '\n' ) continue; if( buf[ 0 ] == '[' ) { /* Begin a section */ if( (endcp = strrchr( buf, ']' )) == NULL ){ camserv_log( "camconfig", "Malformed section on line: %d", lineno ); continue; } cp = &buf[ 1 ]; *endcp = '\0'; if( (current_section = camconfig_add_section( ccfg, cp )) == NULL ){ camserv_log( "camconfig", "Error adding section! (malloc?)"); camconfig_dest( ccfg ); return NULL; } continue; } /* key-val pair */ if( current_section == NULL ){ camserv_log( "camconfig","Line %d not in a section!", lineno ); continue; /* Non-fatal error */ } if( sscanf( buf, "%s %[^\n]s", key, value ) != 2 ){ camserv_log( "camconfig", "Malformed input on line %d", lineno ); continue; } if( section_add_pair( current_section, key, value ) == -1 ){ camserv_log( "camconfig", "Malloc failure adding key-value pair!" ); camconfig_dest( ccfg ); return NULL; } } return ccfg; }
int main_loop( CamConfig *ccfg, Socket *picture_sock, char *picture_mem ){ Socket *listen_socket; SockSet *readset = NULL, *writeset = NULL; list_t *client_sockets; lnode_t *node; int cfg_listen_port, highest_fd, picture_client_ready; int num_sclients, num_clients; ClientInfo *clientinfo, *clientinfo2; if( (client_sockets = list_create( -1 )) == NULL) return -1; cfg_listen_port = camconfig_query_def_int( ccfg, SEC_SOCKET, "listen_port", CAMCONFIG_DEF_LISTEN_PORT ); if( (readset = sockset_new()) == NULL || (writeset = sockset_new()) == NULL ) { camserv_log( MODNAME, "Error allocating memory for socksets!"); if( readset ) sockset_dest( readset ); if( writeset ) sockset_dest( writeset ); list_destroy( client_sockets ); return -1; } if((listen_socket = socket_serve_tcp( NULL, cfg_listen_port, 100 )) == NULL ) { camserv_log( MODNAME, "Error setting up socket on port \"%d\". Exiting", cfg_listen_port ); list_destroy( client_sockets ); sockset_dest( readset ); sockset_dest( writeset ); return -1; } highest_fd = MAX( socket_query_fd( listen_socket ), socket_query_fd( picture_sock )); clientinfo = clientinfo_new( listen_socket ); clientinfo2 = clientinfo_new( picture_sock ); if( !clientinfo || !clientinfo2 || sockset_add_fd( readset, listen_socket, clientinfo ) == -1 || sockset_add_fd( readset, picture_sock, clientinfo2 ) == -1 ) { camserv_log( MODNAME, "Error adding initial sockets to sockset!"); sockset_dest( readset ); sockset_dest( writeset ); if( clientinfo ) clientinfo_dest( clientinfo ); if( clientinfo2 ) clientinfo_dest( clientinfo2 ); list_destroy( client_sockets ); return -1; } num_clients = 0; num_sclients = 0; picture_client_ready = 1; setup_signals(); Abort = 0; while( !Abort ){ int sel_res, i, nset_socks; void **set_socks; /* Only need to execute this if we have a streaming client */ if( (num_sclients > 0) && picture_client_ready == 1 ){ send( socket_query_fd( picture_sock ), "0", sizeof( "0" ), 0 ); picture_client_ready = 0; } sockset_reset( readset ); sockset_reset( writeset ); sel_res = sockset_select( highest_fd + 1, readset, writeset, NULL ); /* Service the event */ if( sel_res == -1 ){ camserv_log( MODNAME, "select() failure: %s", strerror( errno )); break; } else if( sel_res == 0 ){ camserv_log( MODNAME, "Unexpected select() fall through!" ); continue; } /* Readable sockets */ set_socks = sockset_query_socks( readset ); nset_socks = sockset_query_nsocks( readset ); for( i=0; i< nset_socks; i++ ){ ClientInfo *new_cinfo; clientinfo = set_socks[ i ]; if( clientinfo->socket == listen_socket ) { /* New client */ if( (new_cinfo = accept_client( listen_socket )) == NULL ) continue; if( (node = lnode_create( new_cinfo )) == NULL ){ clientinfo_dest( new_cinfo ); continue; } if( sockset_add_fd( readset, new_cinfo->socket, new_cinfo ) == -1 ){ camserv_log( MODNAME, "Failed to add socket %d to socket read set!", socket_query_fd( new_cinfo->socket )); clientinfo_dest( new_cinfo ); lnode_destroy( node ); continue; } if( socket_query_fd( new_cinfo->socket ) > highest_fd ) highest_fd = socket_query_fd( new_cinfo->socket ); list_append( client_sockets, node ); num_clients++; /* Init resource limit for this client */ new_cinfo->create_time = time( NULL ); new_cinfo->bytes = 0; new_cinfo->frames = 0; new_cinfo->max_seconds = camconfig_query_def_int( ccfg, SEC_SOCKET, "max_seconds", 0 ); new_cinfo->max_bytes = camconfig_query_def_int( ccfg, SEC_SOCKET, "max_bytes", 0 ); new_cinfo->max_frames = camconfig_query_def_int( ccfg, SEC_SOCKET, "max_frames", 0 ); /* Send fresh request for a picture */ send( socket_query_fd( picture_sock ), "0", sizeof( "0" ), 0 ); picture_client_ready = 0; /* Put this read socket on hold until the picture comes back */ sockset_hold( readset, new_cinfo->socket ); } else { char cmdbuf[ 1024 ]; int readlen; clientinfo = set_socks[ i ]; /* Regular joe client, set readable */ if( (readlen = read( socket_query_fd( clientinfo->socket), cmdbuf, sizeof( cmdbuf ) - 1)) <= 0 ) { camserv_log( MODNAME, "Closing socket: %s", socket_query_remote_name( clientinfo->socket )); if (clientinfo->client_type == CLIENT_T_BROWSER || clientinfo->client_type == CLIENT_T_PROXY) { num_sclients--; } client_remove( client_sockets, clientinfo ); sockset_del_fd( readset, clientinfo->socket ); sockset_unhold_all( writeset ); sockset_del_fd( writeset, clientinfo->socket ); clientinfo_dest( clientinfo ); num_clients--; } else { if( clientinfo->socket == picture_sock ) { if( dispatch_pictaker( cmdbuf, picture_mem ) == -1 ) camserv_log( MODNAME, "Pictaker dispatch failure!"); sockset_unhold_all( writeset ); /* Release the read hold as the picture has now been taken */ sockset_unhold_all( readset ); picture_client_ready = 1; } else { /* Information from a regular client */ cmdbuf[ readlen ] = '\0'; if( clientinfo->client_type == CLIENT_T_UNINIT ) { char *preamble; int pre_size; /* Figure out what type of client we have */ if( !strncmp( cmdbuf, "GET", 3 )) { if( strstr( cmdbuf, "/singleframe" )) { clientinfo->client_type = CLIENT_T_SINGLE; } else { clientinfo->client_type = CLIENT_T_BROWSER; num_sclients++; } } else if( !strncmp( cmdbuf, "PROXY", 5 )) { clientinfo->client_type = CLIENT_T_PROXY; /* Here we are in the same state as being done writing a pic */ clientinfo->state = CINFO_STATE_PICTURE; num_sclients++; databuf_buf_set( clientinfo->writebuf, NULL, 0 ); } else clientinfo->client_type = CLIENT_T_BROWSER; if( clientinfo->client_type != CLIENT_T_PROXY ) { /* Send the initial preamble. Only now we can decide which type of preamble to send (single vs. multi-part) */ if( clientinfo->client_type == CLIENT_T_SINGLE ) preamble = get_single_preamble_text( &pre_size ); else preamble = get_multi_preamble_text( &pre_size ); databuf_buf_set( clientinfo->writebuf, preamble, pre_size ); } if( sockset_add_fd( writeset, clientinfo->socket, clientinfo ) == -1 ) { camserv_log( MODNAME, "Failed to add socket %d to write set!", socket_query_fd( clientinfo->socket )); } } } } } } if( set_socks != NULL ) free( set_socks ); /* Writable sockets */ set_socks = sockset_query_socks( writeset ); nset_socks = sockset_query_nsocks( writeset ); for( i=0; i< nset_socks; i++ ){ ClientInfo *cinfo; cinfo = set_socks[ i ]; if( cinfo->client_type == CLIENT_T_BROWSER || cinfo->client_type == CLIENT_T_SINGLE ) { int result; if( (result = write_regular_client( cinfo, writeset )) != 0 ){ /* result: 1=close requested, -1=error detected */ if( result == -1 ) camserv_log( MODNAME, "Databuf write error on socket: %s\n", socket_query_remote_name( cinfo->socket )); if (cinfo->client_type == CLIENT_T_BROWSER) { num_sclients--; } client_remove( client_sockets, cinfo ); sockset_del_fd( readset, cinfo->socket ); sockset_del_fd( writeset, cinfo->socket ); clientinfo_dest( cinfo ); num_clients--; } } else { if( write_proxy_client( cinfo, writeset ) == -1 ){ camserv_log( MODNAME, "Databuf write error on socket: %d", socket_query_fd( cinfo->socket )); /* Should be proxy, but better check */ if (cinfo->client_type == CLIENT_T_PROXY) { num_sclients--; } client_remove( client_sockets, cinfo ); sockset_del_fd( readset, cinfo->socket ); sockset_del_fd( writeset, cinfo->socket ); clientinfo_dest( cinfo ); num_clients--; } } } if( set_socks != NULL ) free( set_socks ); } camserv_log( MODNAME, "Aborting."); sockset_dest( readset ); sockset_dest( writeset ); for( node = list_first( client_sockets) ; node; node=list_next( client_sockets, node )) { clientinfo_dest( node->data ); } /* Tell the picture taker to get out! Get out! */ camserv_log( MODNAME, "Closing picture taker"); send( socket_query_fd( picture_sock ), "9", sizeof( "9" ), 0 ); sleep( 3 ); camserv_log( MODNAME, "done\n"); list_destroy_nodes( client_sockets ); list_destroy( client_sockets ); socket_dest( listen_socket ); return 0; }
static int write_regular_client( ClientInfo *cinfo, SockSet *writeset ){ switch(databuf_write( cinfo->writebuf, socket_query_fd( cinfo->socket ))) { case -1 : /* Error */ return -1; break; case 0: /* All done feeding client the current data */ if( cinfo->state == CINFO_STATE_PREAMBLE || cinfo->state == CINFO_STATE_SEPERATOR ) { char *pic_data; size_t pic_size; int pic_id; cinfo->management_data = manager_new_client( &pic_data, &pic_size, &pic_id ); if( cinfo->management_data == NULL ){ camserv_log( MODNAME, "Error managing client! (Picture may not" " be taken yet)"); /* Wait for the next successful picture to come around */ sockset_hold( writeset, cinfo->socket ); return 0; } if( pic_id == cinfo->last_picture_id ) { manager_dest_client( cinfo->management_data ); cinfo->management_data = NULL; /* Whoa boy! Hold on a second! */ sockset_hold( writeset, cinfo->socket ); return 0; } databuf_buf_set( cinfo->writebuf, pic_data, pic_size ); cinfo->last_picture_id = pic_id; cinfo->state = CINFO_STATE_PICTURE; /* Accounting stuff */ cinfo->frames++; cinfo->bytes += pic_size; } else { /* Just finished sending a picture */ char *sep_data; size_t sep_size; if( manager_dest_client( cinfo->management_data ) == -1 ){ camserv_log( MODNAME, "Error destroying client management!"); } cinfo->management_data = NULL; cinfo->state = CINFO_STATE_SEPERATOR; /* For single-frame clients we are done now, and drop them */ if( cinfo->client_type == CLIENT_T_SINGLE ) return 1; /* Check resource limits */ if( cinfo->max_seconds && (time( NULL ) - cinfo->create_time) > cinfo->max_seconds ) { camserv_log( MODNAME, "Dropping client \"%s\", time limit exceeded", socket_query_remote_name( cinfo->socket )); return 1; } if( cinfo->max_bytes && cinfo->bytes > cinfo->max_bytes ) { camserv_log( MODNAME, "Dropping client \"%s\", byte limit exceeded", socket_query_remote_name( cinfo->socket )); return 1; } if( cinfo->max_frames && cinfo->frames > cinfo->max_frames ) { camserv_log( MODNAME, "Dropping client \"%s\", frame limit exceeded", socket_query_remote_name( cinfo->socket )); return 1; } /* send a seperator */ sep_data = get_seperator_text( &sep_size ); databuf_buf_set( cinfo->writebuf, sep_data, sep_size ); } break; case 1: /* Keep feeding data to the client */ break; } return 0; }
static void setup_signals(){ signal( SIGTERM, sighandler ); camserv_log( MODNAME, "Setup signals"); }
int main( int argc, char *argv[] ){ Socket **localsocks; CamConfig *camcfg; char *shm_segment, tmpbuf[ 1024 ], cfg_path[ MAXPATHLEN ]; int fd, shm_alloc, donecfg; extern int errno; donecfg = 0; if (argc >= 2) { strncpy( cfg_path, argv[ 1 ], sizeof( cfg_path ) ); cfg_path[ sizeof( cfg_path ) - 1 ] = '\0'; camserv_log( "main", "Trying to read config file \"%s\": ", cfg_path); if( (camcfg = read_ccfg( cfg_path )) == NULL ){ camserv_log( "main", "Error reading config \"%s\": %s", cfg_path, strerror( errno )); } else { camserv_log( "main", "Success reading config \"%s\"", cfg_path); donecfg=1; } } else { fprintf( stderr, "camserv v%s - by Jon Travis ([email protected])\n", VERSION ); fprintf( stderr, "Syntax: %s <cfg file>\n", argv[0] ); fprintf( stderr, "Will try %s/camserv.cfg\n", DATDIR); if (!donecfg) { snprintf( cfg_path, sizeof( cfg_path ), "%s/camserv.cfg", DATDIR ); cfg_path[ sizeof( cfg_path ) - 1 ] = '\0'; camserv_log( "main", "Trying to read config file \"%s\": ", cfg_path); if( (camcfg = read_ccfg( cfg_path )) == NULL ){ camserv_log( "main", "Error reading config \"%s\": %s", cfg_path, strerror( errno )); } else { camserv_log( "main", "Success reading config \"%s\"", cfg_path); donecfg=1; } } } if (!donecfg) { camserv_log( "main", "Error finding config file, exit!"); return(-1); } /* If we took a single snapshot, we are all done */ if( snap_single( camcfg )) return 0; if( (localsocks = socket_unix_pair( SOCK_DGRAM )) == NULL ){ camserv_log( "main", "Error creating communication sockets between procs"); return -1; } /* Setup a temp file for making our shm */ strcpy( tmpbuf, "/tmp/CAMSERV_XXXXXX" ); if( (fd = mkstemp( tmpbuf )) == -1 ){ camserv_log( "main", "Couldn't create temporary file: %s", tmpbuf ); strcpy( tmpbuf, argv[ 0 ] ); /* Last resort */ } else { close( fd ); } shm_alloc = camconfig_query_def_int( camcfg, SEC_MAIN, "shm_alloc", PICTURE_MALLOC ); if( shm_alloc < PICTURE_MALLOC ) camserv_log( "main", "Allocated %d bytes for SHM [RISKY RISKY!]", shm_alloc); if( (Shmid = shm_setup( tmpbuf, /* Allocate generous ammount */ shm_alloc, &shm_segment) ) == -1 ){ socket_unix_pair_dest( localsocks ); return -1; } unlink( tmpbuf ); /* Start the picture taker thread */ CPid = picture_taker( shm_segment, PICTURE_MALLOC, camcfg, localsocks[ 0 ]); if( CPid == -1 ){ /* Failure setting up camerastuffs */ camserv_log( "main", "Picture taker could not be created!"); socket_unix_pair_dest( localsocks ); return -1; } if( main_loop( camcfg, localsocks[ 1 ], shm_segment ) == -1 ){ camserv_log( "main", "Main loop exited abnormally"); socket_unix_pair_dest( localsocks ); if( CPid != -1 ) kill( CPid, SIGINT ); return -1; } socket_unix_pair_dest( localsocks ); return 0; }
int video_setup_funcs( CamConfig *ccfg, Video_Funcs *vfuncs ){ void *dlhandle; char key[ 1024 ]; const char *module_path; if( video_query_active_section( ccfg, key, sizeof( key )) == NULL ) return -1; if( (module_path = camconfig_query_str( ccfg, key, "path" )) == NULL ){ camserv_log( MODNAME, "[%s]:path not set!", key ); return -1; } if( (dlhandle = dlopen( module_path, RTLD_LAZY | RTLD_GLOBAL )) == NULL ){ camserv_log( MODNAME, "%s", dlerror()); camserv_log( MODNAME, "Error opening video driver; \"%s\"", module_path ); return -1; } if( !(vfuncs->video_open = dlsym( dlhandle, VIDSYM_OPEN ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_close = dlsym( dlhandle, VIDSYM_CLOSE ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_init = dlsym( dlhandle, VIDSYM_INIT ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_deinit = dlsym( dlhandle, VIDSYM_DEINIT ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_snap = dlsym( dlhandle, VIDSYM_SNAP ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_get_geom = dlsym( dlhandle, VIDSYM_GET_GEOM ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } if( !(vfuncs->video_modinfo_query = dlsym( dlhandle, VIDSYM_MODINFO ))){ camserv_log( MODNAME, dlerror() ); dlclose( dlhandle ); return -1; } return 0; }