/// GSourceFunc function to invoke discover member function at the timed interval. /// This function is called by the g_main_loop mechanism when the rediscover timeout elapses. FSTATIC gboolean _discovery_rediscover(gpointer vself) ///<[in/out] Object to perform discovery on { Discovery* self = CASTTOCLASS(Discovery, vself); return self->discover(self); }
/// Function for finalizing FSTATIC void _packetdecoder_finalize(AssimObj* selfobj) { PacketDecoder* self = CASTTOCLASS(PacketDecoder, selfobj); FREE(self->_frametypemap); self->_frametypemap = NULL; self->_pfinalize(selfobj); }
/// Return the non-const private key with the given id WINEXPORT CryptFramePrivateKey* cryptframe_private_key_by_id(const char* key_id) ///<[in] Key id of the given private key being sought { gpointer ret; INITMAPS; ret = (key_id ? g_hash_table_lookup(private_key_map, key_id): NULL); return (ret ? CASTTOCLASS(CryptFramePrivateKey, ret): NULL); }
/// Return the non-const public key with the given id WINEXPORT CryptFramePublicKey* cryptframe_public_key_by_id(const char* key_id) ///[in] Key id of public key being sought { gpointer ret; INITMAPS; ret = (key_id ? g_hash_table_lookup(public_key_map, key_id): NULL); return (ret ? CASTTOCLASS(CryptFramePublicKey, ret): NULL); }
/// Examine our queues and run anything that needs running. /// (this code is more expensive than it could be, but in practice it may not matter) FSTATIC gboolean _resource_queue_runqueue(gpointer pself) { ResourceQueue* self = CASTTOCLASS(ResourceQueue, pself); GHashTableIter iter; gpointer key; gpointer value; gint64 now = g_get_monotonic_time(); gboolean anyelems = FALSE; DEBUGMSG3("%s.%d: Examining queues", __FUNCTION__, __LINE__); g_hash_table_iter_init(&iter, self->resources); while(g_hash_table_iter_next(&iter, &key, &value)) { GQueue* rsc_q = (GQueue*)value; GList* qelem; gboolean any_running = FALSE; for (qelem=rsc_q->head; NULL != qelem; qelem=qelem->next) { RscQElem* qe = CASTTOCLASS(RscQElem, qelem->data); anyelems = TRUE; if (qe->cmd->is_running) { any_running = TRUE; break; } } if (any_running) { continue; } DEBUGMSG4("%s.%d: No resource jobs are running.", __FUNCTION__, __LINE__); for (qelem=rsc_q->head; NULL != qelem; qelem=qelem->next) { RscQElem* qe = CASTTOCLASS(RscQElem, qelem->data); if (now >= qe->cmd->starttime) { REF(self); // We undo this when process exits self->activechildcnt += 1; qe->cmd->execute(qe->cmd); break; } } } if (!anyelems && self->timerid >= 0) { g_source_remove(self->timerid); self->timerid = -1; } return anyelems; }
/// Return the key_id associated with the given destination address WINEXPORT const char * cryptframe_get_dest_key_id(const NetAddr* destaddr) { gpointer g_receiver_key; CryptFramePublicKey* receiver_key; INITMAPS; g_receiver_key = g_hash_table_lookup(addr_to_public_key_map, destaddr); if (NULL == g_receiver_key) { return NULL; } receiver_key = CASTTOCLASS(CryptFramePublicKey, g_receiver_key); return receiver_key->key_id; }
/// Finalize (free) a CryptCurve25519 object FSTATIC void _cryptcurve25519_finalize(AssimObj* aself) ///< Object to finalize/free { CryptCurve25519* self = CASTTOCLASS(CryptCurve25519, aself); if (self->public_key) { UNREF(self->public_key); } if (self->private_key) { UNREF(self->private_key); } _parentclass_finalize(aself); }
/// Finalize (destructor) function for our CryptFramePublicKey objects FSTATIC void _cryptframe_finalize(AssimObj* aself) ///< object to finalize/destroy { CryptFrame* self = CASTTOCLASS(CryptFrame, aself); if (self->sender_key_id) { g_free(self->sender_key_id); self->sender_key_id = NULL; } if (self->receiver_key_id) { g_free(self->receiver_key_id); self->receiver_key_id = NULL; } _parentclass_finalize(aself); }
/// Function for destroying data when an element is removed from self->resources hash table FSTATIC void _resource_queue_hash_data_destructor(gpointer dataptr) { GQueue* q = (GQueue*) dataptr; GList* l; for (l=q->head; NULL != l; l=l->next) { RscQElem* qelem = CASTTOCLASS(RscQElem, l->data); _resource_queue_qelem_finalize(qelem); l->data = NULL; qelem = NULL; } g_queue_clear(q); q = NULL; dataptr = NULL; }
/// Finalize (destructor) function for our CryptFramePublicKey objects FSTATIC void _cryptframe_publickey_finalize(AssimObj* pubkey) ///< object to finalize/destroy { CryptFramePublicKey* self = CASTTOCLASS(CryptFramePublicKey, pubkey); if (self->key_id) { g_free(self->key_id); self->key_id = NULL; } if (self->public_key) { g_free(self->public_key); self->public_key = NULL; } _assimobj_finalize(pubkey); }
/// Finalize (destructor) function for our CryptFramePrivateKey objects FSTATIC void _cryptframe_privatekey_finalize(AssimObj* privkey) ///< object to finalize/destroy { CryptFramePrivateKey* self = CASTTOCLASS(CryptFramePrivateKey, privkey); if (self->key_id) { g_free(self->key_id); self->key_id = NULL; } if (self->private_key) { g_free(self->private_key); self->private_key = NULL; } _assimobj_finalize(privkey); }
/// Called every second during tests gboolean timeout_agent(gpointer ignored) { ReliableUDP* io = CASTTOCLASS(ReliableUDP, nettransport); (void)ignored; if (nano_hbstats.heartbeat_count > (unsigned)maxpkts) { g_message("QUITTING NOW! (heartbeat count)"); io->_protocol->closeall(io->_protocol); nano_initiate_shutdown(); return FALSE; } return TRUE; }
/// Routine to (fake) validate that we have proper authentication... FSTATIC gboolean test_cma_authentication(const FrameSet*fs, NetAddr* fromaddr) { gpointer maybecrypt = g_slist_nth_data(fs->framelist, 1); Frame* mightbecrypt; (void)fromaddr; /// For our purposes, we don't much care how it's encrypted... g_return_val_if_fail(maybecrypt != NULL, FALSE); mightbecrypt = CASTTOCLASS(Frame, maybecrypt); if (mightbecrypt->type != FRAMETYPE_CRYPTCURVE25519) { DUMP("test_cma_authentication: ", &fs->baseclass, " was BAD"); return FALSE; } return TRUE; }
/// Function to perform setup for child between fork and exec (for UNIX-like systems) /// It doesn't get called under Windows. FSTATIC void _childprocess_setup_child(gpointer childprocess_object) { #ifdef WIN32 (void)childprocess_object; #else ChildProcess* self = CASTTOCLASS(ChildProcess, childprocess_object); # ifdef HAVE_SETPGID setpgid(0,0); # else setpgrp(0, 0); # endif (void)self; #endif }
/// /// Set the encryption key to use when sending to destaddr /// Set destkey to NULL to stop encrypting to that destination WINEXPORT gboolean cryptframe_set_dest_key_id(NetAddr*destaddr, ///< Destination addr,port const char * key_id) ///< Public key id to use when encrypting { CryptFramePublicKey* destkey; INITMAPS; g_return_val_if_fail(NULL != destaddr && NULL != key_id, FALSE); destkey = g_hash_table_lookup(public_key_map, key_id); if (NULL == destkey) { g_critical("%s.%d: No key associated with key id %s" , __FUNCTION__, __LINE__, key_id); return FALSE; } cryptframe_set_dest_public_key(destaddr, CASTTOCLASS(CryptFramePublicKey, destkey)); return TRUE; }
/// Update packet data from the frame FSTATIC void _seqnoframe_updatedata(Frame* fself, ///< object whose data will be put into FrameSet packet gpointer tlvptr, ///< pointer to our current TLV entry gconstpointer pktend, ///< end of packet FrameSet* fs) ///< FrameSet that we're doing this for { SeqnoFrame* self = CASTTOCLASS(SeqnoFrame, fself); // NOTE - this gets rid of the "const" coming out of get_generic_tlv_value... ///@todo add a new get_generic_nonconst_tlv_value() function. guint8* pktpos = get_generic_tlv_nonconst_value(tlvptr, pktend); (void)fs; g_return_if_fail(NULL != pktpos); tlv_set_guint32(pktpos, self->_sessionid, pktend); tlv_set_guint64(pktpos+sizeof(guint32), self->_reqid, pktend); tlv_set_guint16(pktpos+sizeof(guint32)+sizeof(guint64),self->_qid, pktend); }
/// Cancel all outstanding requests FSTATIC void _resource_queue_cancelall(ResourceQueue* self) { GHashTableIter iter; gpointer pkey; gpointer pvalue; g_hash_table_iter_init(&iter, self->resources); while(g_hash_table_iter_next(&iter, &pkey, &pvalue)) { GQueue* q = (GQueue*) pvalue; GList* l; for (l=q->head; NULL != l; l=l->next) { RscQElem* qe = CASTTOCLASS(RscQElem, l->data); _resource_queue_cancel(self, qe->cmd->request); } } }
/// Finalize a ResourceQueue -- RIP FSTATIC void _resource_queue_finalize(AssimObj* aself) { ResourceQueue* self = CASTTOCLASS(ResourceQueue, aself); self->shuttingdown = TRUE; if (self->activechildcnt > 0) { return; } if (self->resources) { g_hash_table_destroy(self->resources); self->resources = NULL; } if (self->timerid >= 0) { g_source_remove(self->timerid); self->timerid = -1; } _assimobj_finalize(&self->baseclass); self = NULL; }
/// Initialize our frame type map. /// Post-condition: Every element of 'frametypemap' is initialized with a valid function pointer. PacketDecoder* packetdecoder_new(guint objsize, const FrameTypeToFrame* framemap, gint mapsize) { gint j; AssimObj* baseobj; PacketDecoder* self; if (objsize < sizeof(PacketDecoder)) { objsize = sizeof(PacketDecoder); } if (NULL == framemap) { framemap = _defaultmap; mapsize = DIMOF(_defaultmap); } baseobj = assimobj_new(objsize); proj_class_register_subclassed(baseobj, "PacketDecoder"); self = CASTTOCLASS(PacketDecoder, baseobj); self->_pfinalize = baseobj->_finalize; baseobj->_finalize = _packetdecoder_finalize; self->pktdata_to_framesetlist = _pktdata_to_framesetlist; self->_maxframetype = 0; self->_framemap = framemap; self->_framemaplen = mapsize; for (j=0; j < self->_framemaplen; ++j) { if (self->_framemap[j].frametype > self->_maxframetype) { self->_maxframetype = self->_framemap[j].frametype; } } self->_frametypemap = MALLOC0((self->_maxframetype+1)*sizeof(gpointer)); for (j=0; j <= self->_maxframetype; ++j) { self->_frametypemap[j] = unknownframe_tlvconstructor; } for (j=0; j < self->_framemaplen; ++j) { self->_frametypemap[self->_framemap[j].frametype] = self->_framemap[j].constructor; } return self; }
/// Construct Frame (SeqnoFrame) object from marshalled packet data Frame* seqnoframe_tlvconstructor(gpointer tlvstart, ///<[in] Start of SeqnoFrame TLV area gconstpointer pktend, ///<[in] first byte past end of packet gpointer* ignorednewpkt, ///<[ignored] replacement packet gpointer* ignoredpktend) ///<[ignored] end of replacement packet { SeqnoFrame* ret; guint16 length = get_generic_tlv_len(tlvstart, pktend); guint16 tlvtype = get_generic_tlv_type(tlvstart, pktend); const guint8* valpos = get_generic_tlv_value(tlvstart, pktend); (void)ignorednewpkt; (void)ignoredpktend; g_return_val_if_fail(length == (sizeof(guint64)+sizeof(guint16)+sizeof(guint32)), NULL); ret = seqnoframe_new(tlvtype, 0); ret->_sessionid = tlv_get_guint32(valpos, pktend); ret->setreqid(ret, tlv_get_guint64(valpos+sizeof(guint32), pktend)); ret->setqid(ret, tlv_get_guint16(valpos+sizeof(guint32)+sizeof(guint64), pktend)); return CASTTOCLASS(Frame, ret); }
/// Function to handle child timeouts. /// It implements a very simple, linear state machine... FSTATIC gboolean _childprocess_timeout(gpointer childprocess_object) { ChildProcess* self; DEBUGMSG("%s:%d Called from timeout for process with user_data = %p" , __FUNCTION__, __LINE__, childprocess_object); self = CASTTOCLASS(ChildProcess, childprocess_object); if ((unsigned)(self->child_state) < DIMOF(signalmap)) { #ifdef WIN32 TerminateProcess(self->child_pid, -1); #else (void)kill(self->child_pid, signalmap[self->child_state].signal); #endif self->timeoutsrc_id = g_timeout_add_seconds ( signalmap[self->child_state].next_timeout , _childprocess_timeout, self); self->child_state += 1; }else{ _childprocess_childexit(self->child_pid, 0xffffffff, self); } return FALSE; }
/// Finalizing function for Discovery objects FSTATIC void _discovery_finalize(AssimObj* gself) ///<[in/out] Object to finalize (free) { Discovery* self = CASTTOCLASS(Discovery, gself); char * instancename = self->_instancename; if (self->_timerid > 0) { g_source_remove(self->_timerid); self->_timerid = 0; } if (self->_config) { UNREF(self->_config); } if (_discovery_timers && instancename) { self->_instancename = NULL; // Avoid infinite recursion... g_hash_table_remove(_discovery_timers, instancename); } if (instancename) { g_free(instancename); self->_instancename = instancename = NULL; } FREECLASSOBJ(self); self=NULL; }
/// Routine to pretend to be the initial CMA void fakecma_startup(AuthListener* auth, FrameSet* ifs, NetAddr* nanoaddr) { FrameSet* pkt; NetGSource* netpkt = auth->baseclass.transport; char * nanostr = nanoaddr->baseclass.toString(nanoaddr); GSList* thisgsf; const char * keyid = NULL; (void)ifs; g_message("CMA received startup message from nanoprobe at address %s/%d." , nanostr, nanoaddr->port(nanoaddr)); g_free(nanostr); nanostr = NULL; check_JSON(ifs); netpkt->_netio->addalias(netpkt->_netio, nanoaddr, destaddr); // Set up our crypto... cryptframe_set_dest_key_id(nanoaddr, cryptframe_get_signing_key_id()); cryptframe_set_dest_key_id(destaddr, cryptframe_get_signing_key_id()); cryptframe_associate_identity(CMA_IDENTITY_NAME, cryptframe_get_signing_key_id()); cryptframe_set_encryption_method(cryptcurve25519_new_generic); for (thisgsf = ifs->framelist; thisgsf; thisgsf=thisgsf->next) { Frame* thisframe = CASTTOCLASS(Frame, thisgsf->data); if (thisframe->type == FRAMETYPE_KEYID) { CstringFrame* csf = CASTTOCLASS(CstringFrame, thisframe); keyid = (const char *)csf->baseclass.value; }else if (keyid && thisframe->type == FRAMETYPE_PUBKEYCURVE25519) { cryptcurve25519_save_public_key(keyid, thisframe->value , thisframe->length); } } // Send the configuration data to our new "client" pkt = create_setconfig(nanoconfig); netpkt->_netio->sendareliablefs(netpkt->_netio, nanoaddr, DEFAULT_FSP_QID, pkt); UNREF(pkt); // Now tell them to send/expect heartbeats to various places pkt = create_sendexpecthb(auth->baseclass.config, FRAMESETTYPE_SENDEXPECTHB, destaddr, 1); netpkt->_netio->sendareliablefs(netpkt->_netio, nanoaddr, DEFAULT_FSP_QID, pkt); UNREF(pkt); { const char * monopjson[] = { START, MONITOR, STOP}; unsigned j; // Create a frameset for a few resource operations pkt = frameset_new(FRAMESETTYPE_DORSCOP); for (j=0; j < DIMOF(monopjson); j++) { CstringFrame* csf = cstringframe_new(FRAMETYPE_RSCJSON,0); csf->baseclass.setvalue(&csf->baseclass, g_strdup(monopjson[j]) , strlen(monopjson[j])+1, g_free); frameset_append_frame(pkt, &csf->baseclass); UNREF2(csf); } netpkt->_netio->sendareliablefs(netpkt->_netio, nanoaddr, DEFAULT_FSP_QID, pkt); UNREF(pkt); } { const char * discoverjson[] = {SWITCHDISCOVER, ARPDISCOVER}; const char * discoverinstnm[] = {SWINSTNM, ARPINSTNM}; unsigned j; // Create a frameset for a few discovery operations pkt = frameset_new(FRAMESETTYPE_DODISCOVER); for (j=0; j < DIMOF(discoverjson); j++) { CstringFrame* csf = cstringframe_new(FRAMETYPE_DISCJSON,0); CstringFrame* inst = cstringframe_new(FRAMETYPE_DISCNAME,0); fprintf(stderr, "Creating discovery frameset: %s: %s\n" , discoverinstnm[j] , discoverjson[j]); inst->baseclass.setvalue(&inst->baseclass, g_strdup(discoverjson[j]) , strlen(discoverjson[j])+1, g_free); frameset_append_frame(pkt, &inst->baseclass); UNREF2(inst); csf->baseclass.setvalue(&csf->baseclass, g_strdup(discoverjson[j]) , strlen(discoverjson[j])+1, g_free); frameset_append_frame(pkt, &csf->baseclass); UNREF2(csf); } netpkt->_netio->sendareliablefs(netpkt->_netio, nanoaddr, DEFAULT_FSP_QID, pkt); UNREF(pkt); } pkt = frameset_new(FRAMESETTYPE_ACKSTARTUP); netpkt->_netio->sendareliablefs(netpkt->_netio, nanoaddr, DEFAULT_FSP_QID, pkt); UNREF(pkt); g_info("ACKSTARTUP packet queued to send"); }
/// Called when an operation completes - it calls requestor's callback if no repeat, /// and requeues it if it is going to repeat FSTATIC void _resource_queue_endnotify ( ConfigContext* request , gpointer user_data , enum HowDied exittype , int rc , int signal , gboolean core_dumped , const char* stringresult) { RscQElem* self = CASTTOCLASS(RscQElem, user_data); ResourceQueue* parent = self->parent; ResourceCmd* cmd = self->cmd; gboolean shouldrepeat = TRUE; g_queue_remove(self->ourQ, self); parent->activechildcnt -= 1; if (parent->shuttingdown && parent->activechildcnt <= 0) { _resource_queue_finalize(&parent->baseclass); return; } DEBUGMSG1("%s.%d: EXIT happened exittype:%d repeat:%d, cancelme:%d", __FUNCTION__, __LINE__ , exittype, self->repeatinterval, self->cancelme); // Should this request repeat? if (self->cancelme || (self->cancelonfail && exittype != EXITED_ZERO) || (0 == self->repeatinterval)) { shouldrepeat = FALSE; }else{ shouldrepeat = TRUE; } // Notify the user that their repeating command flipped status, or has stopped running // (i.e., if it was failing but now works, or was working, but now fails) if (FALSE == shouldrepeat || (exittype == EXITED_ZERO && !cmd->last_success) || (exittype != EXITED_ZERO && cmd->last_success)) { DEBUGMSG1("%s.%d: Calling callback for request id " FMT_64BIT "d." , __FUNCTION__, __LINE__, self->requestid); self->callback(request, self->user_data, exittype, rc, signal, core_dumped , stringresult); if (shouldrepeat && exittype == EXITED_ZERO && stringresult) { g_message("%s: %s", cmd->loggingname, stringresult); } } cmd->last_success = (exittype == EXITED_ZERO); if (shouldrepeat) { DEBUGMSG1("%s.%d: Repeat request id " FMT_64BIT "d.", __FUNCTION__, __LINE__ , self->requestid); self->queuetime = g_get_monotonic_time(); cmd->starttime = self->queuetime + (self->repeatinterval*uSPERSEC); g_queue_push_tail(self->ourQ, self); _resource_queue_runqueue(self->parent); }else{ DEBUGMSG1("%s.%d: Don't repeat request id " FMT_64BIT "d.", __FUNCTION__, __LINE__ , self->requestid); if (g_queue_get_length(self->ourQ) == 0) { g_hash_table_remove(self->parent->resources, cmd->resourcename); } _resource_queue_runqueue(self->parent); _resource_queue_qelem_finalize(self); self = NULL; } // Undo the ref we did before starting this job UNREF(parent); }
/// /// We update the data in the packet from our CryptCurve25519 object with the /// side-effect of encrypting all the frames already put into the packet. Note that /// this only works because we always construct the packet from the end back to the /// beginning. We do this in-place - fortunately the algorithms allow that... /// We effectively suck all the remaining frames into a single encrypted frame... FSTATIC void _cryptcurve25519_updatedata(Frame* f, ///< Frame to marshall gpointer tlvstart, ///< Start of our Frame in the packet gconstpointer pktend, ///< Last byte in the allocated packet FrameSet* unused_fs) ///< Pointer to our containing frameset { CryptCurve25519*self = CASTTOCLASS(CryptCurve25519, f); const guint8* pktend8 = pktend; //guint8* tlvstart8 = tlvstart; guint8* tlvval; guint8* valptr; guint32 plaintextoffset; guint32 plaintextsize; guint32 cyphertextoffset; guint32 nonceoffset; guint32 tlvsize; unsigned char* nonce; int j; (void)unused_fs; // [key1, key2, nonce, MAC, plaintext] DUMP3(__FUNCTION__, &f->baseclass, " is CryptCurve25519 Frame being processed."); DEBUGMSG3("%s.%d: tlvstart:%p, pktend:%p", __FUNCTION__, __LINE__, tlvstart, pktend); // The plain text starts immediately after our (incoming) frame plaintextoffset = f->length; // Plain text starts here cyphertextoffset = plaintextoffset - crypto_box_MACBYTES; // Preceded by MAC nonceoffset = cyphertextoffset - crypto_box_NONCEBYTES; // Preceded by nonce // Our (outgoing) frame consists of the original incoming frame plus all other frames after ours tlvval = get_generic_tlv_nonconst_value(tlvstart, pktend); tlvsize = pktend8 - tlvval; plaintextsize = (tlvsize - plaintextoffset); // Generate a "nonce" as part of the packet - make known plaintext attacks harder // ... lots of our plaintext is easy to figure out ... nonce = tlvval + nonceoffset; DEBUGMSG3("%s.%d: generating random nonce (%p, %d, %p)", __FUNCTION__, __LINE__ , nonce, (int)crypto_box_NONCEBYTES, nonce+crypto_box_NONCEBYTES); randombytes_buf(nonce, crypto_box_NONCEBYTES); DEBUGMSG3("%s.%d: random nonce generated.", __FUNCTION__, __LINE__); DEBUGMSG3("%s.%d: public->key_id: [%s], private_key->key_id: [%s]", __FUNCTION__, __LINE__ , self->public_key->key_id, self->private_key->key_id); DEBUGMSG3("%s.%d: calling crypto_box_easy(%p,%p,%d,%p,%p,%p)", __FUNCTION__, __LINE__ , tlvval+cyphertextoffset, tlvval+plaintextoffset, plaintextsize , nonce, self->public_key->public_key, self->private_key->private_key); DEBUGCKSUM4("plain text cksum:", tlvval+plaintextoffset, plaintextsize); DEBUGCKSUM4("receiver public key cksum:",self->public_key -> public_key, crypto_box_PUBLICKEYBYTES); DEBUGCKSUM4("sender private key cksum:", self->private_key->private_key, crypto_box_SECRETKEYBYTES); DEBUGCKSUM4("nonce cksum:", nonce, crypto_box_NONCEBYTES); // Encrypt in-place [we previously allocated enough space for authentication info] crypto_box_easy(tlvval+cyphertextoffset, tlvval+plaintextoffset, plaintextsize , nonce, self->public_key->public_key, self->private_key->private_key); DEBUGMSG4("cypher offset versus tlvstart: %ld", (long)(tlvval+cyphertextoffset-(guint8*)tlvstart)); DEBUGCKSUM4("cypher text checksum:", tlvval+cyphertextoffset, plaintextsize+crypto_box_MACBYTES); set_generic_tlv_type(tlvstart, self->baseclass.baseclass.type, pktend); set_generic_tlv_len(tlvstart, tlvsize, pktend); // Put in the frame type, length, key name length, and key name for both keys // We're the sender - our [private] key name goes first, then the receiver's [public] key name valptr = get_generic_tlv_nonconst_value(tlvstart, pktend); for (j=0; j < 2; ++j) { char * key_id = (j == 0 ? self->baseclass.sender_key_id : self->baseclass.receiver_key_id); int keylen = strlen(key_id)+1; tlv_set_guint8(valptr, keylen, pktend); valptr += 1; g_strlcpy((char *)valptr, key_id, keylen); valptr += keylen; } DEBUGMSG3("%s.%d: returning after next assert (tlvval:%p, tlvsize%d, pktend:%p" , __FUNCTION__, __LINE__, tlvval, (int)tlvsize, pktend); g_assert((tlvval + tlvsize) == pktend); DEBUGMSG3("%s.%d: returning (assert passed).", __FUNCTION__, __LINE__); }
/// Function called when the child (finally) exits... FSTATIC void _childprocess_childexit(GPid pid, gint status, gpointer childprocess_object) { ChildProcess* self = CASTTOCLASS(ChildProcess, childprocess_object); gboolean signalled = WIFSIGNALED(status); int exitrc = 0; int signal = 0; gboolean logexit = FALSE; enum HowDied howwedied = NOT_EXITED; (void)pid; if (self->timeoutsrc_id > 0) { g_source_remove(self->timeoutsrc_id); DEBUGMSG3("%s.%d: Removed timeout %d for process with user_data = %p" , __FUNCTION__, __LINE__, self->timeoutsrc_id, self); self->timeoutsrc_id = 0; } // If it refused to die, then the status is invalid if ((guint)(self->child_state) >= DIMOF(signalmap)) { howwedied = EXITED_HUNG; }else if ((guint)self->child_state != CHILDSTATE_RUNNING) { // Then we tried to kill it... howwedied = EXITED_TIMEOUT; signal = signalled ? WTERMSIG(status) : 0; }else{ if (signalled) { signal = WTERMSIG(status); howwedied = EXITED_SIGNAL; }else{ exitrc = WEXITSTATUS(status); howwedied = (exitrc == 0 ? EXITED_ZERO : EXITED_NONZERO); } } switch (howwedied) { case EXITED_SIGNAL: /*FALLTHROUGH*/ case EXITED_TIMEOUT: /*FALLTHROUGH*/ case EXITED_HUNG: logexit = self->logmode > CHILD_NOLOG; break; case EXITED_NONZERO: logexit = self->logmode >= CHILD_LOGERRS; break; case EXITED_ZERO: logexit = self->logmode >= CHILD_LOGALL; break; default: // We'll never produce any other values above /*NOTREACHED*/ logexit = TRUE; break; } if (logexit) { switch (howwedied) { case EXITED_SIGNAL: g_warning("Child process [%s] died from signal %d%s." , self->loggingname, signal, WCOREDUMP(status) ? " (core dumped)" : ""); break; case EXITED_TIMEOUT: if (signalled) { g_warning("Child process [%s] timed out after %d seconds [signal %d%s]." , self->loggingname, self->timeout, signal , WCOREDUMP(status) ? " (core dumped)" : ""); }else{ g_warning("Child process [%s] timed out after %d seconds." , self->loggingname, self->timeout); } break; case EXITED_HUNG: g_warning("Child process [%s] timed out after %d seconds and could not be killed." , self->loggingname, self->timeout); break; case EXITED_NONZERO: g_message("Child process [%s] exited with return code %d." , self->loggingname, exitrc); break; case EXITED_ZERO: g_message("Child process [%s] exited normally.", self->loggingname); break; default:/*NOTREACHED*/ break; } } DEBUGMSG2("%s.%d: Exit happened howwedied:%d", __FUNCTION__, __LINE__ , howwedied); if (!self->stdout_src->atEOF) { //DEBUGMSG3("Child %d [%s] EXITED but output is not at EOF [fd%d]", pid //, self->loggingname, self->stdout_src->gfd.fd); self->stdout_src->readmore(self->stdout_src); } if (!self->stderr_src->baseclass.atEOF) { self->stderr_src->baseclass.readmore(&self->stderr_src->baseclass); } self->notify(self, howwedied, exitrc, signal, WCOREDUMP(status)); self->child_state = -1; DEBUGMSG5("%s.%d: UNREF child: %p", __FUNCTION__,__LINE__, self); UNREF(self); // Undo the REF(self) in our constructor }