static void *start_relay_stream (void *arg) { client_t *client = arg; relay_server *relay; source_t *src; int failed = 1, sources; global_lock(); sources = ++global.sources; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* set the start time, because we want to decrease the sources on all failures */ client->connection.con_time = time (NULL); do { ice_config_t *config = config_get_config(); relay = client->shared_data; src = relay->source; thread_mutex_lock (&src->lock); src->flags |= SOURCE_PAUSE_LISTENERS; if (sources > config->source_limit) { config_release_config(); WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount); break; } config_release_config(); INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount); if (open_relay (relay) < 0) break; stats_event_inc (NULL, "source_relay_connections"); source_init (src); failed = 0; } while (0); client->ops = &relay_client_ops; client->schedule_ms = timing_get_time(); if (failed) { /* failed to start any connection, better clean up and reset */ if (relay->on_demand) src->flags &= ~SOURCE_ON_DEMAND; else { yp_remove (relay->localmount); src->yp_public = -1; } relay->in_use = NULL; INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners); src->flags &= ~SOURCE_PAUSE_LISTENERS; thread_mutex_unlock (&src->lock); } thread_spin_lock (&relay_start_lock); relays_connecting--; thread_spin_unlock (&relay_start_lock); client->flags |= CLIENT_ACTIVE; worker_wakeup (client->worker); return NULL; }
/* factoring out code for stats loops ** this function copies all stats to queue, and registers */ static void _register_listener (client_t *client) { event_listener_t *listener = client->shared_data; avl_node *node; stats_event_t stats_count; refbuf_t *refbuf; size_t size = 8192; char buffer[20]; build_event (&stats_count, NULL, "stats_connections", buffer); stats_count.action = STATS_EVENT_INC; process_event (&stats_count); /* first we fill our queue with the current stats */ refbuf = refbuf_new (size); refbuf->len = 0; /* the global stats */ avl_tree_rlock (_stats.global_tree); node = avl_get_first(_stats.global_tree); while (node) { stats_node_t *stat = node->key; if (stat->flags & listener->mask) { if (_append_to_buffer (refbuf, size, "EVENT global %s %s\n", stat->name, stat->value) < 0) { _add_node_to_stats_client (client, refbuf); refbuf = refbuf_new (size); refbuf->len = 0; continue; } } node = avl_get_next(node); } avl_tree_unlock (_stats.global_tree); /* now the stats for each source */ avl_tree_rlock (_stats.source_tree); node = avl_get_first(_stats.source_tree); while (node) { avl_node *node2; stats_node_t *metadata_stat = NULL; stats_source_t *snode = (stats_source_t *)node->key; if (snode->flags & listener->mask) { stats_node_t *ct = _find_node (snode->stats_tree, "content-type"); const char *type = "audio/mpeg"; if (ct) type = ct->name; if (_append_to_buffer (refbuf, size, "NEW %s %s\n", type, snode->source) < 0) { _add_node_to_stats_client (client, refbuf); refbuf = refbuf_new (size); refbuf->len = 0; continue; } } node = avl_get_next(node); avl_tree_rlock (snode->stats_tree); node2 = avl_get_first(snode->stats_tree); while (node2) { stats_node_t *stat = node2->key; if (metadata_stat == NULL && strcmp (stat->name, "metadata_updated") == 0) metadata_stat = stat; else if (stat->flags & listener->mask) { if (_append_to_buffer (refbuf, size, "EVENT %s %s %s\n", snode->source, stat->name, stat->value) < 0) { _add_node_to_stats_client (client, refbuf); refbuf = refbuf_new (size); refbuf->len = 0; continue; } } node2 = avl_get_next (node2); } while (metadata_stat) { if (_append_to_buffer (refbuf, size, "EVENT %s %s %s\n", snode->source, metadata_stat->name, metadata_stat->value) < 0) { _add_node_to_stats_client (client, refbuf); refbuf = refbuf_new (size); refbuf->len = 0; continue; } break; } avl_tree_unlock (snode->stats_tree); } avl_tree_unlock (_stats.source_tree); _add_node_to_stats_client (client, refbuf); /* now we register to receive future event notices */ thread_mutex_lock (&_stats.listeners_lock); listener->next = _stats.event_listeners; _stats.event_listeners = listener; thread_mutex_unlock (&_stats.listeners_lock); }
/* Actually open the connection and do some http parsing, handle any 302 * responses within here. */ static int open_relay_connection (client_t *client, relay_server *relay, relay_server_master *master) { int redirects = 0; http_parser_t *parser = NULL; connection_t *con = &client->connection; char *server = strdup (master->ip); char *mount = strdup (master->mount); int port = master->port, timeout = master->timeout, ask_for_metadata = relay->mp3metadata; char *auth_header = NULL; if (relay->username && relay->password) { char *esc_authorisation; unsigned len = strlen(relay->username) + strlen(relay->password) + 2; DEBUG2 ("using username %s for %s", relay->username, relay->localmount); auth_header = malloc (len); snprintf (auth_header, len, "%s:%s", relay->username, relay->password); esc_authorisation = util_base64_encode(auth_header); free(auth_header); len = strlen (esc_authorisation) + 24; auth_header = malloc (len); snprintf (auth_header, len, "Authorization: Basic %s\r\n", esc_authorisation); free(esc_authorisation); } while (redirects < 10) { sock_t streamsock; char *bind = NULL; /* policy decision, we assume a source bind even after redirect, possible option */ if (master->bind) bind = strdup (master->bind); if (bind) INFO4 ("connecting to %s:%d for %s, bound to %s", server, port, relay->localmount, bind); else INFO3 ("connecting to %s:%d for %s", server, port, relay->localmount); con->con_time = time (NULL); relay->in_use = master; streamsock = sock_connect_wto_bind (server, port, bind, timeout); free (bind); if (connection_init (con, streamsock, server) < 0) { WARN2 ("Failed to connect to %s:%d", server, port); break; } parser = get_relay_response (con, mount, server, ask_for_metadata, auth_header); if (parser == NULL) { ERROR4 ("Problem trying to start relay on %s (%s:%d%s)", relay->localmount, server, port, mount); break; } if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0) { /* better retry the connection again but with different details */ const char *uri, *mountpoint; int len; uri = httpp_getvar (parser, "location"); INFO1 ("redirect received %s", uri); if (strncmp (uri, "http://", 7) != 0) break; uri += 7; mountpoint = strchr (uri, '/'); free (mount); if (mountpoint) mount = strdup (mountpoint); else mount = strdup ("/"); len = strcspn (uri, ":/"); port = 80; if (uri [len] == ':') port = atoi (uri+len+1); free (server); server = calloc (1, len+1); strncpy (server, uri, len); connection_close (con); httpp_destroy (parser); parser = NULL; } else { if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ERROR2("Error from relay request: %s (%s)", relay->localmount, httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); client->parser = NULL; break; } sock_set_blocking (streamsock, 0); thread_mutex_lock (&relay->source->lock); client->parser = parser; // old parser will be free in the format clear thread_mutex_unlock (&relay->source->lock); client->connection.discon_time = 0; client->connection.con_time = time (NULL); client_set_queue (client, NULL); free (server); free (mount); free (auth_header); return 0; } redirects++; } /* failed, better clean up */ free (server); free (mount); free (auth_header); if (parser) httpp_destroy (parser); connection_close (con); con->con_time = time (NULL); if (relay->in_use) relay->in_use->skip = 1; return -1; }
/* Move clients from source to dest provided dest is running * and that the stream format is the same. * The only lock that should be held when this is called is the * source tree lock */ void source_move_clients (source_t *source, source_t *dest) { /* we don't want the two write locks to deadlock in here */ thread_mutex_lock (&move_clients_mutex); /* if the destination is not running then we can't move clients */ if (dest->running == 0) { WARN1 ("destination mount %s not running, unable to move clients ", dest->mount); thread_mutex_unlock (&move_clients_mutex); return; } avl_tree_wlock (dest->pending_tree); do { client_t *client; /* we need to move the client and pending trees */ avl_tree_wlock (source->pending_tree); if (source->format == NULL) { INFO1 ("source mount %s is not available", source->mount); break; } if (source->format->type != dest->format->type) { WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount); break; } while (1) { avl_node *node = avl_get_first (source->pending_tree); if (node == NULL) break; client = (client_t *)(node->key); avl_delete (source->pending_tree, client, NULL); /* switch client to different queue */ client_set_queue (client, dest->stream_data_tail); avl_insert (dest->pending_tree, (void *)client); } avl_tree_wlock (source->client_tree); while (1) { avl_node *node = avl_get_first (source->client_tree); if (node == NULL) break; client = (client_t *)(node->key); avl_delete (source->client_tree, client, NULL); /* switch client to different queue */ client_set_queue (client, dest->stream_data_tail); avl_insert (dest->pending_tree, (void *)client); } source->listeners = 0; stats_event (source->mount, "listeners", "0"); avl_tree_unlock (source->client_tree); } while (0); avl_tree_unlock (source->pending_tree); avl_tree_unlock (dest->pending_tree); thread_mutex_unlock (&move_clients_mutex); }
static int stats_listeners_send (client_t *client) { int loop = 8, total = 0; int ret = 0; event_listener_t *listener = client->shared_data; if (client->connection.error || global.running != ICE_RUNNING) return -1; if (client->refbuf && client->refbuf->flags & STATS_BLOCK_CONNECTION) loop = 4; else /* allow for 200k lag but only after 2Meg has been sent, give connection time * to cacth up after the large dump at the beginning */ if (client->connection.sent_bytes > 2000000 && listener->content_len > 200000) { WARN1 ("dropping stats client, %ld in queue", listener->content_len); return -1; } client->schedule_ms = client->worker->time_ms; thread_mutex_lock (&_stats.listeners_lock); while (1) { refbuf_t *refbuf = client->refbuf; if (refbuf == NULL) { client->schedule_ms = client->worker->time_ms + 60; break; } if (loop == 0 || total > 32768) break; ret = format_generic_write_to_client (client); if (ret > 0) { total += ret; listener->content_len -= ret; } if (client->pos == refbuf->len) { client->refbuf = refbuf->next; refbuf->next = NULL; refbuf_release (refbuf); client->pos = 0; if (client->refbuf == NULL) { if (listener->content_len) WARN1 ("content length is %u", listener->content_len); listener->recent_block = NULL; client->schedule_ms = client->worker->time_ms + 50; break; } loop--; } else { client->schedule_ms = client->worker->time_ms + 200; break; /* short write, so stop for now */ } } thread_mutex_unlock (&_stats.listeners_lock); if (client->connection.error || global.running != ICE_RUNNING) return -1; return 0; }
/*! \fn void *snmp_host_init(int host_id, char *hostname, int snmp_version, * char *snmp_community, char *snmp_username, char *snmp_password, int snmp_port, * int snmp_timeout) * \brief initializes an snmp_session object for a Cactid host * * This function will initialize NET-SNMP or UCD-SNMP for the Cactid host * in question. * */ void *snmp_host_init(int host_id, char *hostname, int snmp_version, char *snmp_community, char *snmp_username, char *snmp_password, int snmp_port, int snmp_timeout) { void *sessp = NULL; struct snmp_session session; char hostnameport[BUFSIZE]; /* initialize SNMP */ snmp_sess_init(&session); #ifdef USE_NET_SNMP /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1); #endif /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1); #endif #ifdef NETSNMP_DS_LIB_DONT_PRINT_UNITS netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PRINT_UNITS, 1); #endif netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_BARE_VALUE, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NUMERIC_TIMETICKS, 1); #else ds_set_boolean(DS_LIBRARY_ID, DS_LIB_QUICK_PRINT, 1); ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_BARE_VALUE, 1); ds_set_boolean(DS_LIBRARY_ID, DS_LIB_NUMERIC_TIMETICKS, 1); #endif /* verify snmp version is accurate */ if (snmp_version == 2) { session.version = SNMP_VERSION_2c; }else if (snmp_version == 1) { session.version = SNMP_VERSION_1; }else if (snmp_version == 3) { session.version = SNMP_VERSION_3; }else { CACTID_LOG(("Host[%i] ERROR: SNMP Version Error for Host '%s'\n", host_id, hostname)); return 0; } snprintf(hostnameport, sizeof(hostnameport), "%s:%i", hostname, snmp_port); session.peername = hostnameport; session.retries = 3; session.remote_port = snmp_port; session.timeout = (snmp_timeout * 1000); /* net-snmp likes microseconds */ if ((snmp_version == 2) || (snmp_version == 1)) { session.community = snmp_community; session.community_len = strlen(snmp_community); }else { /* set the SNMPv3 user name */ session.securityName = snmp_username; session.securityNameLen = strlen(session.securityName); session.securityAuthKeyLen = USM_AUTH_KU_LEN; /* set the authentication method to MD5 */ session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, OIDSIZE(usmHMACMD5AuthProtocol)); session.securityAuthProtoLen = OIDSIZE(usmHMACMD5AuthProtocol); /* set the privacy protocol to none */ session.securityPrivProto = usmNoPrivProtocol; session.securityPrivProtoLen = OIDSIZE(usmNoPrivProtocol); session.securityPrivKeyLen = USM_PRIV_KU_LEN; /* set the security level to authenticate, but not encrypted */ session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; /* set the authentication key to the hashed version. The password must me at least 8 char */ if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *) snmp_password, strlen(snmp_password), session.securityAuthKey, &(session.securityAuthKeyLen)) != SNMPERR_SUCCESS) { CACTID_LOG(("SNMP: Error generating SNMPv3 Ku from authentication pass phrase.")); } } /* open SNMP Session */ thread_mutex_lock(LOCK_SNMP); sessp = snmp_sess_open(&session); thread_mutex_unlock(LOCK_SNMP); if (!sessp) { CACTID_LOG(("ERROR: Problem initializing SNMP session '%s'\n", hostname)); } return sessp; }
void global_lock(void) { thread_mutex_lock(&_global_mutex); }
static void tls_mem_free(const char *filename, int line, void *ptr) { MBLOCK *real_ptr; size_t len; CHECK_IN_PTR2(ptr, real_ptr, len, filename, line); #if 1 if (real_ptr->mem_slice->tid != (unsigned long) acl_pthread_self()) { #else if (real_ptr->mem_slice->tid != mem_slice->tid) { #endif MUTEX_LOCK(real_ptr->mem_slice); PRIVATE_ARRAY_PUSH(real_ptr->mem_slice->list, real_ptr); MUTEX_UNLOCK(real_ptr->mem_slice); } else acl_slice_pool_free(filename, line, real_ptr); } static void *tls_mem_alloc(const char *filename, int line, size_t len) { const char *myname = "tls_mem_alloc"; ACL_MEM_SLICE *mem_slice = acl_pthread_getspecific(__mem_slice_key); char *ptr; MBLOCK *real_ptr; if (mem_slice == NULL) { /* 每个子线程获得自己的线程局部存储内存池 */ mem_slice = mem_slice_create(); mem_slice->slice_list = __mem_slice_list; /* 将子线程的线程局部存储内存池置入全局内存池句柄集合中 */ if (__mem_slice_list_lock) thread_mutex_lock(__mem_slice_list_lock); private_array_push(__mem_slice_list, mem_slice); if (__mem_slice_list_lock) thread_mutex_unlock(__mem_slice_list_lock); } real_ptr = (MBLOCK *) acl_slice_pool_alloc(filename, line, mem_slice->slice_pool, SPACE_FOR(len)); if (real_ptr == 0) { acl_msg_error("%s(%d): malloc: insufficient memory", myname, __LINE__); return 0; } mem_slice->nalloc++; if (mem_slice->nalloc == mem_slice->nalloc_gc) { mem_slice->nalloc = 0; mem_slice_gc(mem_slice); } CHECK_OUT_PTR(ptr, real_ptr, mem_slice, len); return ptr; } static void *tls_mem_calloc(const char *filename, int line, size_t nmemb, size_t size) { void *ptr = tls_mem_alloc(filename, line, nmemb * size); memset(ptr, 0, nmemb * size); return ptr; }
static int update_from_master(ice_config_t *config) { char *master = NULL, *password = NULL, *username= NULL; int port; sock_t mastersock; int ret = 0; char buf[256]; do { char *authheader, *data; relay_server *new_relays = NULL, *cleanup_relays; int len, count = 1; username = strdup ("relay"); if (config->master_password) password = strdup (config->master_password); if (config->master_server) master = strdup (config->master_server); port = config->master_server_port; if (password == NULL || master == NULL || port == 0) break; ret = 1; config_release_config(); mastersock = sock_connect_wto (master, port, 0); if (mastersock == SOCK_ERROR) { WARN0("Relay slave failed to contact master server to fetch stream list"); break; } len = strlen(username) + strlen(password) + 2; authheader = malloc(len); snprintf (authheader, len, "%s:%s", username, password); data = util_base64_encode(authheader); sock_write (mastersock, "GET /admin/streamlist.txt HTTP/1.0\r\n" "Authorization: Basic %s\r\n" "\r\n", data); free(authheader); free(data); if (sock_read_line(mastersock, buf, sizeof(buf)) == 0 || strncmp (buf, "HTTP/1.0 200", 12) != 0) { sock_close (mastersock); WARN0 ("Master rejected streamlist request"); break; } while (sock_read_line(mastersock, buf, sizeof(buf))) { if (!strlen(buf)) break; } while (sock_read_line(mastersock, buf, sizeof(buf))) { relay_server *r; if (!strlen(buf)) continue; DEBUG2 ("read %d from master \"%s\"", count++, buf); r = calloc (1, sizeof (relay_server)); if (r) { r->server = xmlStrdup (master); r->port = port; r->mount = xmlStrdup (buf); r->localmount = xmlStrdup (buf); r->mp3metadata = 1; r->next = new_relays; new_relays = r; } } sock_close (mastersock); thread_mutex_lock (&(config_locks()->relay_lock)); cleanup_relays = update_relays (&global.master_relays, new_relays); relay_check_streams (global.master_relays, cleanup_relays); relay_check_streams (NULL, new_relays); thread_mutex_unlock (&(config_locks()->relay_lock)); } while(0); if (master) free (master); if (username) free (username); if (password) free (password); return ret; }
static void ptmutex_acquire(mr_lock_t l) { thread_mutex_lock(l); }