Beispiel #1
0
void ZMServer::handleHello()
{
    // just send OK so the client knows all is well
    // followed by the protocol version we understand
    string outStr("");
    ADD_STR(outStr, "OK");
    ADD_STR(outStr, ZM_PROTOCOL_VERSION);
    send(outStr);
}
Beispiel #2
0
static dpl_status_t
get_delete_data_content(dpl_ctx_t *ctx, dpl_locators_t *locators, dpl_sbuf_t *buffer)
{
  int           i;
  dpl_status_t  ret;

#define DPL_FN(fn, args...) ({                  \
      ret = fn(args);                           \
      if (ret != DPL_SUCCESS)                   \
        return ret;                             \
    })
#define ADD_STR(str) DPL_FN(dpl_sbuf_add_str, buffer, str)
#define ADD_STR_FMT(fmt, args...) DPL_FN(dpl_sbuf_add_str_fmt, buffer, fmt, args)

  ADD_STR("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          "<Delete>\n"
          "    <Quiet>false</Quiet>\n");

  for (i = 0; i < locators->size; i++) {
    dpl_locator_t       *locator = &locators->tab[i];

    ADD_STR("    <Object>\n");
    ADD_STR_FMT("        <Key>%s</Key>\n", locator->name);
    if (locator->version_id != NULL)
      ADD_STR_FMT("        <VersionId>%s</VersionId>\n", locator->version_id);
    ADD_STR("    </Object>\n");
  }

  ADD_STR("</Delete>");

#undef DPL_FN
#undef ADD_STR
#undef ADD_STR_FMT

  return DPL_SUCCESS;
}
Beispiel #3
0
void ZMServer::sendError(string error)
{
    string outStr("");
    ADD_STR(outStr, string("ERROR - ") + error);
    send(outStr);
}
Beispiel #4
0
/* Caller has to free the return value. */
char *stack_trace(conf_object_t *cpu, int eip, int tid)
{
	char *buf = MM_XMALLOC(MAX_TRACE_LEN, char);
	int pos = 0, old_pos;
	int stack_offset = 0; /* Counts by 1 - READ_STACK already multiplies */

	ADD_STR(buf, pos, MAX_TRACE_LEN, "TID%d at 0x%.8x in ", tid, eip);
	ADD_FRAME(buf, pos, MAX_TRACE_LEN, eip);

	int stop_ebp = 0;
	int ebp = GET_CPU_ATTR(cpu, ebp);
	int rabbit = ebp;
	int frame_count = 0;

	while (ebp != 0 && (unsigned)ebp < USER_MEM_START && frame_count++ < 1024) {
		bool extra_frame;

		do {
			int eip_offset;
			bool iret_block = false;

			extra_frame = false;
			/* at the beginning or end of a function, there is no
			 * frame, but a return address is still on the stack. */
			if (function_eip_offset(eip, &eip_offset)) {
				if (eip_offset == 0) {
					extra_frame = true;
				} else if (eip_offset == 1 &&
				           READ_BYTE(cpu, eip - 1)
				           == OPCODE_PUSH_EBP) {
					stack_offset++;
					extra_frame = true;
				}
			}
			if (!extra_frame) {
				int opcode = READ_BYTE(cpu, eip);
				if (opcode == OPCODE_RET) {
					extra_frame = true;
				} else if (opcode == OPCODE_IRET) {
					iret_block = true;
					extra_frame = true;
				}
			}
			if (extra_frame) {
				eip = READ_STACK(cpu, stack_offset);
				ADD_STR(buf, pos, MAX_TRACE_LEN, "%s0x%.8x in ",
					STACK_TRACE_SEPARATOR, eip);
				ADD_FRAME(buf, pos, MAX_TRACE_LEN, eip);
				if (iret_block)
					stack_offset += IRET_BLOCK_WORDS;
				else
					stack_offset++;;
			}
		} while (extra_frame);

		/* pushed return address behind the base pointer */
		eip = READ_MEMORY(cpu, ebp + WORD_SIZE);
		stack_offset = ebp + 2;
		ADD_STR(buf, pos, MAX_TRACE_LEN, "%s0x%.8x in ",
			STACK_TRACE_SEPARATOR, eip);
		old_pos = pos;
		ADD_FRAME(buf, pos, MAX_TRACE_LEN, eip);
		/* special-case termination condition */
		if (pos - old_pos >= strlen(ENTRY_POINT) &&
		    strncmp(buf + old_pos, ENTRY_POINT,
		            strlen(ENTRY_POINT)) == 0) {
			break;
		}

		if (rabbit != stop_ebp) rabbit = READ_MEMORY(cpu, ebp);
		if (rabbit == ebp) stop_ebp = ebp;
		if (rabbit != stop_ebp) rabbit = READ_MEMORY(cpu, ebp);
		if (rabbit == ebp) stop_ebp = ebp;
		ebp = READ_MEMORY(cpu, ebp);
	}

	char *buf2 = MM_XSTRDUP(buf); /* truncate to save space */
	MM_FREE(buf);

	return buf2;
}
int CMakeSip::addAuth(char *un, char *pwd, SIP_MSG *psMsg)
{
   
   HASHHEX HA1;
   HASHHEX HA2 = "";
   HASHHEX respHex32;
   HDR_AUT * hdrAuth=&psMsg->hdrProxyAuthen;
   
   if (psMsg==NULL || un==NULL || pwd==NULL)
      return -1;
   DEBUG_T(0,"add proxy auth");
   
   if(psMsg->sipHdr.dstrStatusCode.uiVal==401)
   {
      hdrAuth=&sMsg->hdrWWWAuth;
   }
   char *pCNonce=hdrAuth->dstrQOP.strVal?(char *)"6ad34fc1":NULL;
   char *pNC=(char *)"00000001";
   
   
   if (false==DigestCalcHA1(hdrAuth,(unsigned char *)un,(unsigned char *)pwd, HA1))
      return -2;
   if (false==DigestCalcResponse(HA1, hdrAuth,psMsg->hdrCSeq.uiMethodID,pCNonce,pNC, &strDstAddr, HA2, respHex32))
      return -2;
   if(psMsg->sipHdr.dstrStatusCode.uiVal==407)
   {
      ADD_STR(buf,uiLen,"Proxy-Authorization: ");
   }
   else
   {
      ADD_STR(buf,uiLen,"Authorization: ");
   }
   
   //"
   //TODO add MD5 or MD5 sess
   uiLen+= (unsigned int)sprintf(buf+uiLen,
                                 "Digest username=\"%s\", realm=\"%.*s\", nonce=\"%.*s\", uri=\"%.*s\", response=\"%.*s\""
                                 ,un
                                 ,D_STR(hdrAuth->dstrRealm)
                                 ,D_STR(hdrAuth->dstrNonce)
                                 ,strDstAddr.len,strDstAddr.s
                                 ,32,respHex32
                                 );
   
   if(hdrAuth->iFlag & 4)
   {
      uiLen+= (unsigned int)sprintf(buf+uiLen,", algorithm=%.*s",D_STR(hdrAuth->dstrAlgo));
   }
   
   if(hdrAuth->iFlag & 128)
   {
      uiLen+= (unsigned int)sprintf(buf+uiLen,", opaque=\"%.*s\"",D_STR(hdrAuth->dstrOpaque));
   }
   if(hdrAuth->iFlag & 32)
   {
      uiLen+= (unsigned int)sprintf(buf+uiLen,", qop=\"%.*s\"",D_STR(hdrAuth->dstrQOP));
      if(pNC)
      {
         uiLen+= (unsigned int)sprintf(buf+uiLen,", nc=%s",pNC);
      }
      if(pCNonce)
      {
         uiLen+= (unsigned int)sprintf(buf+uiLen,", cnonce=%s",pCNonce);
      }
   }
   
   ADD_CRLF(buf,uiLen);
   
   return 0;
}
int CMakeSip::makeReq(int id, PHONE_CFG * cfg, ADDR *addrExt,STR_64 *str64ExtADDR)
{
   unsigned int iMeth=0;
   unsigned int uiFromLoc=0;
   unsigned int uiFromLen=0;
   strDstAddr.s=(unsigned char *)&spSes->dstSipAddr.strVal;
   strDstAddr.len=(int)spSes->dstSipAddr.uiLen;
   
   
   if(cfg){
      
      if(strcmp(&cfg->szSipTransport[0],"TLS")==0){iIsTCP=0;iIsTLS=1;}
      else if(strcmp(&cfg->szSipTransport[0],"TCP")==0){iIsTCP=1;iIsTLS=0;}
      else if(strcmp(&cfg->szSipTransport[0],"UDP")==0){iIsTCP=0;iIsTLS=0;}
      else {iIsTCP=0;iIsTLS=0;}
      
   }
   
   char *s=buf;
   
   int iPrevId=spSes->cs.iSendS;
   spSes->cs.iSendS=id;
   if(id!=METHOD_ACK)
   {
      if(iPrevId!=spSes->cs.iSendS)
      {
         if(spSes->cs.iSendS==METHOD_OPTIONS){
            spSes->sSendTo.setRetransmit(2,5*T_GT_SECOND);
         }
         else if(iIsTCP || iIsTLS)
            spSes->sSendTo.setRetransmit(7,5*T_GT_SECOND);
         else if(spSes->cs.iSendS==METHOD_REGISTER)
            spSes->sSendTo.setRetransmit(7,1*T_GT_SECOND);
         else  if(spSes->cs.iSendS==METHOD_INVITE)
            spSes->sSendTo.setRetransmit(7,(cfg==NULL || cfg->iNetworkIsMobile)?(6*T_GT_SECOND):(3*T_GT_SECOND));
         else
            spSes->sSendTo.setRetransmit();
      }
      spSes->cs.iWaitS=200;
   }
   else
   {
      spSes->cs.iWaitS=0;
   }
   
   //TODO getITFromZZ
   unsigned int uiCSeq;
#if 1
   if(cfg && id==METHOD_REGISTER)
   {
      if(!cfg->uiSipCSeq){
         //next reg should be with greater CSeq, even app restarts, if call-id is same
         //problem was with SIP Thor on OpenSIPS XS 1.4.5, but not  with FreeSwitch
         
         int get_time();
         cfg->uiSipCSeq=(unsigned int)get_time();
         cfg->uiSipCSeq-=1000000000;
      }
      cfg->uiSipCSeq++;
      uiCSeq=(unsigned int)cfg->uiSipCSeq;
   }
   else if(spSes){
      if(id!=METHOD_ACK && id!=METHOD_CANCEL)spSes->uiSipCseq++;
      uiCSeq=spSes->uiSipCseq;
   }
   else {
      uiCSeq=getTickCount();
      while(uiCSeq>1000000000)uiCSeq-=1000000000;
   }
#else
   static unsigned int  ss=User::TickCount();
   ss++;
   uiCSeq=ss;
   
#endif
   if(!uiCSeq)uiCSeq=1;

   int iReplaceRoute=0;
   
   if(iContactId >(int)sMsg->hldContact.uiCount)
      iContactId=0;

   GET_METH(id,iMeth);
   
   //TODO do we need to send route with cancel msg
#define METHOD_CANCEL_IGNORE 0
   
   struct HOLDER_CONTACT *hc = &sMsg->hldContact;
   HLD_ROUTE *hrr = &sMsg->hldRecRoute;
   
   //do i need to check "&& spSes->sSIPMsg.hldRecRoute.uiCount"?
   if(hc->uiCount<1 && spSes && spSes->sSIPMsg.hldContact.uiCount && sMsg->sipHdr.dstrStatusCode.uiVal>=300){
      hc = &spSes->sSIPMsg.hldContact;
      hrr = &spSes->sSIPMsg.hldRecRoute;
   }
   
   
   if(hc->x[iContactId].sipUri.dstrSipAddr.uiLen && (id & (METHOD_CANCEL_IGNORE|METHOD_REGISTER))==0)
   {
      if(hrr->uiCount)//TODO caller calle
      {
         iReplaceRoute=!hasRouteLR(hrr,0);
      }
      if(iReplaceRoute)
      {
         strDstAddr.s=(unsigned char *)hrr->x[0].sipUri.dstrSipAddr.strVal;
         strDstAddr.len=(int)hrr->x[0].sipUri.dstrSipAddr.uiLen;
      }
      else
      {
         strDstAddr.s=(unsigned char *)hc->x[iContactId].sipUri.dstrSipAddr.strVal;
         strDstAddr.len=(int)hc->x[iContactId].sipUri.dstrSipAddr.uiLen;
      }
   }
   //HDR
   ADD_DSTR(s,uiLen,sip_meth[iMeth]); ADD_CHAR(s,uiLen,' ');
   ADD_L_STR(s,uiLen,strDstAddr.s, strDstAddr.len);ADD_0_STR(s,uiLen," SIP/2.0\r\n");
   
   switch(id)
   {
      case METHOD_INVITE:
         sMsg->hdrCSeq.dstrID.uiVal=uiCSeq;
         break;
      case METHOD_ACK:
      case METHOD_CANCEL:
         uiCSeq=sMsg->hdrCSeq.dstrID.uiVal;
         break;
         
      case METHOD_REFER:
         //  ADD_STR(s,uiLen,"Refer-To: <");
         //ADD_DSTR(s,uiLen,spSes->str32Forward);
         //ADD_0_STR(s,uiLen,">\r\n");
         break;
         
      case METHOD_REGISTER:
         if(sMsg->hdrCSeq.dstrID.uiVal)uiCSeq=sMsg->hdrCSeq.dstrID.uiVal+1;//ast
         sMsg->hdrCSeq.dstrID.uiVal=uiCSeq;
      case METHOD_OPTIONS:
         
         ADD_STR(s,uiLen,"Allow: INVITE,ACK,CANCEL,OPTIONS,MESSAGE,BYE,INFO\r\n");
         if(id==METHOD_OPTIONS)
         {
            ADD_STR(s,uiLen,"Accept: application/sdp, text/plain\r\n");
         }
         break;
         
   }
   //TODO test asterisk add
   if((id & METHOD_REGISTER) && cfg && !cfg->reg.bUnReg && cfg->iUseOnlyNatIp==0 && sMsg &&  sMsg->hldVia.x[0].dstrRecived.uiLen<32)
   {
      if(sMsg->hldVia.x[0].dstrRecived.strVal && sMsg->hldVia.x[0].dstrRecived.uiLen){
         //TODO hi addr
         
         if(str64ExtADDR)
            spSes->pIPVisble=str64ExtADDR;
         
         memcpy(spSes->pIPVisble->strVal
                ,sMsg->hldVia.x[0].dstrRecived.strVal
                ,sMsg->hldVia.x[0].dstrRecived.uiLen);
         
         spSes->pIPVisble->uiLen=sMsg->hldVia.x[0].dstrRecived.uiLen;
         
         if(sMsg->hldVia.x[0].dstrRPort.uiVal)
            spSes->uiUserVisibleSipPort=sMsg->hldVia.x[0].dstrRPort.uiVal;
         
         if(addrExt)
         {
            addrExt->ip=ipstr2long(sMsg->hldVia.x[0].dstrRecived.uiLen,sMsg->hldVia.x[0].dstrRecived.strVal);
            
            SWAP_INT(addrExt->ip);
            addrExt->setPort((unsigned int)spSes->uiUserVisibleSipPort);
            
            
         }
      }
      else if(sMsg->hldVia.x[0].dstrRPort.uiVal){
         //  printf("[reg rport=%d ]",sMsg->hldVia.x[0].dstrRPort.uiVal);
         spSes->uiUserVisibleSipPort=sMsg->hldVia.x[0].dstrRPort.uiVal;
         addrExt->setPort((unsigned int)spSes->uiUserVisibleSipPort);
      }
   }
   
   if(iIsTCP)ADD_STR(s,uiLen,"Via: SIP/2.0/TCP ")
      else if(iIsTLS)ADD_STR(s,uiLen,"Via: SIP/2.0/TLS ")
         else ADD_STR(s,uiLen,"Via: SIP/2.0/UDP ")
            ADD_DSTR(s,uiLen,(*spSes->pIPVisble));
   
   if(spSes->uiUserVisibleSipPort!=DEAFULT_SIP_PORT)
      uiLen += sprintf(s + uiLen, ":%u", spSes->uiUserVisibleSipPort);
   
   ADD_STR(s,uiLen,";rport");
   ADD_STR(s,uiLen,";branch=z9hG4bK");
   ADD_L_STR(s,uiLen,sMsg->dstrCallID.strVal,4);
   if(id!=METHOD_CANCEL)
   {
      ADD_L_STR(s,uiLen,s,2);
   }
   else
   {
      ADD_STR(s,uiLen,"IN");
   }
   
   uiLen += sprintf(s + uiLen, "%u",uiCSeq^0x123);
   
   
   
   
   ADD_CRLF(s,uiLen);
   
   uiLen += sprintf(s + uiLen, "CSeq: %u ", uiCSeq);
   ADD_DSTRCRLF(s,uiLen,sip_meth[iMeth])
   
   
   
   if((id & (METHOD_REGISTER|METHOD_CANCEL))
      || (spSes->cs.iCaller  && (sMsg==NULL || sMsg->hdrCSeq.uiMethodID==0)))
   {
      
      ADD_FSTR(s,uiLen,"From: ",6);
      {
         uiFromLoc=uiLen;
         if(cfg->user.nick[0]){
            ADD_CHAR(s,uiLen,'"');
            ADD_0_STR(s,uiLen,cfg->user.nick);
            ADD_CHAR(s,uiLen,'"');
         }
         uiLen+=sprintf(s+uiLen," <%.*s>",D_STR(spSes->userSipAddr));
         
         uiFromLen=uiLen-uiFromLoc;
         
         if(spSes->str16Tag.uiLen)
         {
            ADD_0_STR(s,uiLen,";tag=");
            ADD_DSTRCRLF(s,uiLen,spSes->str16Tag);
         }
         else ADD_CRLF(s,uiLen);
         
      }
      {
         
         if((sMsg->hdrTo.dstrTag.uiLen==0 && id!=METHOD_REGISTER)
            || (id & METHOD_CANCEL)
            )
         {
            //  puts("a4");
            ADD_FSTR(s,uiLen,"To: <",5);
            //vajag lai straadaatu 3xx vispaar sho laikam jau nevajag,
            //jo es jau iedoshu liidzi sMsg kuru saneemu
            if(spSes && spSes->sSIPMsg.hdrTo.sipUri.dstrSipAddr.uiLen==0)
            {
               spSes->sSIPMsg.hdrTo.sipUri.dstrSipAddr.strVal=
               (char *)&spSes->sSIPMsg.rawDataBuffer+spSes->sSIPMsg.uiOffset;
               ADD_DSTR((char *)&spSes->sSIPMsg.rawDataBuffer, spSes->sSIPMsg.uiOffset,spSes->dstSipAddr);
               
               spSes->sSIPMsg.hdrTo.sipUri.dstrSipAddr.uiLen=spSes->dstSipAddr.uiLen;
               //varbuut sho saglabaat pie get new ses
            }
            if(sMsg->hdrTo.sipUri.dstrSipAddr.strVal)
            {
               ADD_DSTR(s,uiLen,sMsg->hdrTo.sipUri.dstrSipAddr);
            }
            else
            {
               ADD_DSTR(s,uiLen,spSes->dstSipAddr);
            }
            ADD_FSTR(s,uiLen,">\r\n",3);
            
            // puts("a5");
         }
         else if (id==METHOD_REGISTER)
         {
            //  DEBUG(0,"make register---------")
            ADD_FSTR(s,uiLen,"To: ",4);
            memcpy(s+uiLen,s+uiFromLoc,uiFromLen);
            uiLen+=uiFromLen;
            ADD_CRLF(s,uiLen);
         }
         else
         {
            ADD_DSTRCRLF(s,uiLen,sMsg->hdrTo.dstrFullRow);
            //  puts("a6");
         }
      }
   }
   else
   {
      //TODO use full row
      //      char *pMeth1;
      //    char *pMeth2;
      DSTR *fromAddr;
      DSTR *toAddr;
      DSTR *tag;
      //   puts("a7");
      
      if(sMsg->sipHdr.dstrStatusCode.uiVal)
      {
         fromAddr=&sMsg->hdrFrom.sipUri.dstrSipAddr;
         toAddr=&sMsg->hdrTo.sipUri.dstrSipAddr;
         tag=&sMsg->hdrTo.dstrTag;
         //    puts("a8");
      }
      else
      {
         toAddr=&sMsg->hdrFrom.sipUri.dstrSipAddr;
         fromAddr=&sMsg->hdrTo.sipUri.dstrSipAddr;
         tag=&sMsg->hdrFrom.dstrTag;
         //  puts("a9");
      }
      
      uiLen+=sprintf(s+uiLen,"From: <%.*s>", D_STR(*fromAddr));
      if(spSes->str16Tag.uiLen)
      {
         ADD_0_STR(s,uiLen,";tag=");
         ADD_DSTRCRLF(s,uiLen,spSes->str16Tag);
      }
      else ADD_CRLF(s,uiLen);
      

      uiLen+=sprintf(s+uiLen,"To: <%.*s>", D_STR(*toAddr));
      
      if(tag->uiLen && !fToTagIgnore)
      {
         ADD_0_STR(s,uiLen,";tag=");
         ADD_DSTRCRLF(s,uiLen,(*tag));
      }
      else ADD_CRLF(s,uiLen);
   }
   
   ADD_FSTR(s,uiLen,"Call-ID: ",9);
   ADD_DSTRCRLF(s,uiLen,sMsg->dstrCallID);
   ADD_STR(s,uiLen,"Max-Forwards: 70\r\n");
   
   if(id==METHOD_REGISTER){
      if(cfg && cfg->szUA[0]){
         ADD_STR(s,uiLen,"User-Agent: ");
         ADD_0_STR(s,uiLen,cfg->szUA);ADD_CRLF(s,uiLen);
      }
      else{
         ADD_STR(s,uiLen,USER_AGENT);
      }
   }
   
   
   if(id != METHOD_CANCEL_IGNORE && (hrr->uiCount || iReplaceRoute))//(toMake & (METHOD_BYE |METHOD_ACK)) && ???
   {
      
      uiLen+=addRoute(s+uiLen, 255, hrr, "Route: ",
                      !((sMsg->hdrCSeq.uiMethodID && sMsg->sipHdr.dstrStatusCode.uiVal) ||
                        (spSes->cs.iCaller && sMsg->hdrCSeq.uiMethodID==0))
                      ,iReplaceRoute, &hc->x[iContactId].sipUri.dstrSipAddr);
      
   }
   
   if(id & (METHOD_INVITE | METHOD_OPTIONS |METHOD_MESSAGE |METHOD_REGISTER|METHOD_REFER|METHOD_SUBSCRIBE|METHOD_PUBLISH|METHOD_UPDATE))
   {
      //unsigned int uiPos;
      if(cfg && cfg->user.nick[0]){
         uiLen+=sprintf(s+uiLen,"Contact: \"%s\" <sip:",cfg->user.nick);//sUserCfg.szUserName);
      }
      else {ADD_STR(s,uiLen,"Contact: <sip:");}
      //uiPos=uiLen;
      
      //TODO if register store contact addr and use it until next reg
      
      if(cfg  && (cfg->isOnline() || (id & METHOD_REGISTER)))
      {
         if(*cfg->user.nr)
            uiLen+=sprintf(s+uiLen,"%s@",cfg->user.nr);
         else if(*cfg->user.un)
            uiLen+=sprintf(s+uiLen,"%s@",cfg->user.un);
      }
      
      ADD_DSTR(s,uiLen,(*spSes->pIPVisble));
      
      
      if(spSes->uiUserVisibleSipPort!=DEAFULT_SIP_PORT)
         uiLen+=sprintf(s+uiLen,":%u",spSes->uiUserVisibleSipPort);
      
      
      
      if(iIsTCP) {ADD_STR(s,uiLen,";transport=tcp>")}else
         if(iIsTLS) {ADD_STR(s,uiLen,";transport=tls>")} else
         { ADD_FSTR(s,uiLen,">",1)}
Beispiel #7
0
/*
 * action_execve adds an exec node to the subgraph and sets WAITLESS_PARENT
 * to the hash of the arguments.  The first node in the child process will
 * use WAITLESS_PARENT as its first parent node.  Note that WAITLESS_PARENT
 * is intentionally _not_ the same as the exec node; this encodes the idea
 * that child processes depend on their parent processes only through the
 * arguments to execve (and the current directory).
 *
 * In the case of shared spines (due to pipes or other IPC mechanisms) the
 * exec node encodes only the path without argv and envp and WAITLESS_PARENT
 * has the format #id where id is a SYSV shared memory id.  This allows further
 * subgraph nodes from child and parent to be interleaved.  Since in the shared
 * case the child _does_ descend directly from the exec node, an explicit
 * record of argv and envp would be redundant.
 */
int action_execve(const char *path, const char *const argv[], const char *const envp[])
{
    fd_map_dump();
    struct process *process = lock_master_process();
    int linked = process != process_info();
    wlog("exec: linked %d", linked);

    // Pack all the arguments into a single buffer.  The format is
    //     char path[];
    //     uint32_t argc;
    //     char argv[argc][];
    //     char is_pipe;
    //     uint32_t envc;
    //     char envp[envc][];
    //     char cwd[];
    // with all strings packed together with terminating nulls.
    char data[4096];
    char *p = data;
#define ADD_STR(s) p += strlcpy(p, (s), data+sizeof(data)-p) + 1
    ADD_STR(path);
    // encode argv
    char *cp = p;
    p += sizeof(uint32_t); // skip 4 bytes for len(argv)
    uint32_t i;
    for (i = 0; argv[i]; i++)
        ADD_STR(argv[i]);
    memcpy(cp, &i, sizeof(uint32_t));
    *p++ = linked;
    // encode envp
    cp = p;
    p += sizeof(uint32_t); // skip 4 bytes for len(envp)
    uint32_t count;
    for (i = 0; envp[i]; i++)
        if (!startswith(envp[i], "WAITLESS")) {
            ADD_STR(envp[i]);
            count++;
        }
    memcpy(cp, &count, sizeof(uint32_t));
    // encode pwd
    if (!real_getcwd(p, data+sizeof(data)-p))
        die("action_execve: getcwd failed: %s", strerror(errno));
    int n = p - data + strlen(p) + 1;
#undef ADD_STR

    // Store exec data and create a corresponding exec node
    struct hash data_hash;
    remember_hash_memory(&data_hash, data, n);
    new_node(process, SG_EXEC, &data_hash);

    // Add the program to the snapshot
    struct hash path_hash, program_hash;
    remember_hash_path(&path_hash, path);
    struct snapshot_entry *entry = snapshot_update(&program_hash, path, &path_hash, 1);
    if (entry->writing)
        die("can't exec '%s' while it is being written", path); // TODO: block instead of dying
    entry->read = 1;
    shared_map_unlock(&snapshot);

    // TODO: Run ldd/otool and hash all shared library dependencies as well
    // TODO: Complain if the program is statically linked
    // TODO: If we decide it's worth it, parse #! lines and hash interpreters
    // too.  That seems easy enough to probably be worth it.

    // If not linked, set parents to the new child values
    if (!linked) {
        process->parents.n = 2; 
        process->parents.p[0] = data_hash;
        process->parents.p[1] = program_hash;
    }

    unlock_master_process();

    // Update process flags
    process = lock_process();
    int old_flags = process->flags;
    process->flags = 0;
    p = rindex(path, '/');
    const char *name = p ? p+1 : path;
    if (!strcmp(name, "as"))
        process->flags |= HACK_SKIP_O_STAT;
    else if (strstr(name, "-gcc-")) {
        for (i = 1; argv[i]; i++)
            if (!strcmp(argv[i], "-c")) {
                process->flags |= HACK_SKIP_O_STAT;
                break;
            }
    }
    unlock_process();

    // Do the exec
    int ret = real_execve(path, argv, envp);

    // An error must have occurred; reset flags back to old value
    process = lock_process();
    process->flags = old_flags;
    unlock_process();
    return ret;
}