/* length (4), PASS_THROUGH (1), header, message */ int ei_ei_send_reg_encoded(ei_cnode* ec, int fd, const erlang_pid *from, const char *to, const char *msg, int msglen) { char *s, header[1400]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ #ifdef HAVE_WRITEV struct iovec v[2]; #endif /* are we tracing? */ /* check that he can receive trace tokens first */ if (ei_distversion(fd) > 0) token = ei_trace(0,(erlang_trace *)NULL); /* header = REG_SEND, from, cookie, toname max sizes: */ ei_encode_version(header,&index); /* 1 */ if (token) { ei_encode_tuple_header(header,&index,5); /* 2 */ ei_encode_long(header,&index,ERL_REG_SEND_TT); /* 2 */ } else { ei_encode_tuple_header(header,&index,4); ei_encode_long(header,&index,ERL_REG_SEND); } ei_encode_pid(header,&index,from); /* 268 */ ei_encode_atom(header,&index,"" /*ei_getfdcookie(ec, fd)*/ ); /* 258 */ ei_encode_atom(header,&index,to); /* 268 */ if (token) ei_encode_trace(header,&index,token); /* 534 */ /* control message (precedes header actually) */ /* length = 1 ('p') + header len + message len */ s = header; put32be(s, index + msglen - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1336 */ #ifdef DEBUG_DIST if (ei_trace_distribution > 0) ei_show_sendmsg(stderr,header,msg); #endif #ifdef HAVE_WRITEV v[0].iov_base = (char *)header; v[0].iov_len = index; v[1].iov_base = (char *)msg; v[1].iov_len = msglen; if (writev(fd,v,2) != index+msglen) return -1; #else /* no writev() */ if (writesocket(fd,header,index) != index) return -1; if (writesocket(fd,msg,msglen) != msglen) return -1; #endif return 0; }
ETERM *erl_global_whereis(int fd, const char *name, char *node) { char buf[EISMALLBUF]; char *bufp=buf; char tmpbuf[64]; int index = 0; erlang_pid *self = erl_self(); erlang_pid epid; ETERM *opid; erlang_msg msg; int i; int version,arity,msglen; self->num = fd; /* FIXME looks strange to change something?! */ ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,"global"); /* Mod */ ei_encode_atom(buf,&index,"whereis_name"); /* Fun */ ei_encode_list_header(buf,&index,1); /* Args: [ name ] */ ei_encode_atom(buf,&index,name); ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return NULL; while (1) { index = EISMALLBUF; if (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) continue; else break; } if (i != ERL_SEND) return NULL; /* expecting { rex, pid } */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_decode_pid(buf,&index,&epid)) return NULL; /* bad response from other side */ /* put the pid into a format for the caller */ index = 0; ei_encode_pid(buf,&index,&epid); opid = erl_decode((unsigned char*)buf); /* extract the nodename for the caller */ if (node) strcpy(node,epid.node); return opid; }
/** * @brief Encode {error, <error_atom>} for the common error response * @param buf where to store the atom * @param index the index into buf * @param error_atom the atom's name * @return 0 on success; -1 on error */ int erlcmd_encode_error_tuple(char *buf, int *index, const char *error_atom) { if (ei_encode_tuple_header(buf, index, 2) == 0 && ei_encode_atom(buf, index, "error") == 0 && ei_encode_atom(buf, index, error_atom) == 0) return 0; else return -1; }
/** * @brief Send a response of the form {:error, reason} * * @param reason a reason (sent back as an atom) */ static void send_error_response(const char *reason) { char resp[256]; int resp_index = sizeof(uint16_t); // Space for payload size ei_encode_version(resp, &resp_index); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "error"); ei_encode_atom(resp, &resp_index, reason); erlcmd_send(resp, resp_index); }
static ErlDrvSSizeT encode_error(char* buf, char* error) { int index = 0; if (ei_encode_version(buf, &index) || ei_encode_tuple_header(buf, &index, 2) || ei_encode_atom(buf, &index, "error") || ei_encode_atom(buf, &index, error)) { return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL; } return index+1; }
static void enumerate_ports() { struct serial_info *port_list = find_serialports(); int port_list_len = 0; for (struct serial_info *port = port_list; port != NULL; port = port->next) port_list_len++; debug("Found %d ports", port_list_len); char resp[4096]; int resp_index = sizeof(uint16_t); // Space for payload size resp[resp_index++] = response_id; ei_encode_version(resp, &resp_index); ei_encode_map_header(resp, &resp_index, port_list_len); for (struct serial_info *port = port_list; port != NULL; port = port->next) { ei_encode_binary(resp, &resp_index, port->name, strlen(port->name)); int info_count = (port->description ? 1 : 0) + (port->manufacturer ? 1 : 0) + (port->serial_number ? 1 : 0) + (port->vid > 0 ? 1 : 0) + (port->pid > 0 ? 1 : 0); ei_encode_map_header(resp, &resp_index, info_count); if (port->description) { ei_encode_atom(resp, &resp_index, "description"); ei_encode_binary(resp, &resp_index, port->description, strlen(port->description)); } if (port->manufacturer) { ei_encode_atom(resp, &resp_index, "manufacturer"); ei_encode_binary(resp, &resp_index, port->manufacturer, strlen(port->manufacturer)); } if (port->serial_number) { ei_encode_atom(resp, &resp_index, "serial_number"); ei_encode_binary(resp, &resp_index, port->serial_number, strlen(port->serial_number)); } if (port->vid > 0) { ei_encode_atom(resp, &resp_index, "vendor_id"); ei_encode_ulong(resp, &resp_index, port->vid); } if (port->pid > 0) { ei_encode_atom(resp, &resp_index, "product_id"); ei_encode_ulong(resp, &resp_index, port->pid); } } erlcmd_send(resp, resp_index); serial_info_free_list(port_list); }
static void handle_signals(const char *req, int *req_index) { // No arguments (void) req; (void) req_index; if (!uart_is_open(uart)) { send_error_response("ebadf"); return; } struct uart_signals sig; if (uart_get_signals(uart, &sig) >= 0) { char resp[128]; int resp_index = sizeof(uint16_t); resp[resp_index++] = response_id; ei_encode_version(resp, &resp_index); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "ok"); ei_encode_map_header(resp, &resp_index, 8); encode_kv_bool(resp, &resp_index, "dsr", sig.dsr); encode_kv_bool(resp, &resp_index, "dtr", sig.dtr); encode_kv_bool(resp, &resp_index, "rts", sig.rts); encode_kv_bool(resp, &resp_index, "st", sig.st); encode_kv_bool(resp, &resp_index, "sr", sig.sr); encode_kv_bool(resp, &resp_index, "cts", sig.cts); encode_kv_bool(resp, &resp_index, "cd", sig.cd); encode_kv_bool(resp, &resp_index, "rng", sig.rng); erlcmd_send(resp, resp_index); } else send_error_response(uart_last_error()); }
static int mn_send_delete(int fd, erlang_pid *mnesia, const char *key) { char sbuf[EISMALLBUF]; char *dbuf = NULL; char *msgbuf; int index = 0; int len = strlen(key) + 32; /* 32 is a slight overestimate */ if (len > EISMALLBUF) if (!(dbuf = malloc(len))) return -1; msgbuf = (dbuf ? dbuf : sbuf); /* set up delete message { delete, Key } */ ei_encode_version(msgbuf,&index); ei_encode_tuple_header(msgbuf,&index,2); ei_encode_atom(msgbuf,&index,EI_MNESIA_DELETE); ei_encode_string(msgbuf,&index,key); /* send it */ if (ei_send_encoded(fd,mnesia,msgbuf,index)) { if (dbuf) free(dbuf); return -1; } if (dbuf) free(dbuf); return 0; }
/** * @brief Send :ok back to Elixir */ static void send_ok_response() { char resp[256]; int resp_index = sizeof(uint16_t); // Space for payload size ei_encode_version(resp, &resp_index); ei_encode_atom(resp, &resp_index, "ok"); erlcmd_send(resp, resp_index); }
/* Called in active mode when there's a read or an error */ static void handle_notify_read(int error_reason, const uint8_t *data, size_t len) { char *resp = malloc(64 + len); int resp_index = sizeof(uint16_t); ei_encode_version(resp, &resp_index); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "notif"); if (error_reason == 0) { // Normal receive ei_encode_binary(resp, &resp_index, data, len); } else { // Error notification ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "error"); ei_encode_atom(resp, &resp_index, uart_last_error()); } erlcmd_send(resp, resp_index); free(resp); }
static int mn_send_write(int fd, erlang_pid *mnesia, const char *key, ei_reg_obj *obj) { char sbuf[EISMALLBUF]; char *dbuf = NULL; char *msgbuf; int index = 0; int keylen = strlen(key) + 1; int len = 32 + keylen + obj->size; if (len > EISMALLBUF) if (!(dbuf = malloc(len))) return -1; msgbuf = (dbuf ? dbuf : sbuf); ei_encode_version(msgbuf,&index); ei_encode_tuple_header(msgbuf,&index,6); ei_encode_atom(msgbuf,&index,EI_MNESIA_WRITE); ei_encode_string(msgbuf,&index,key); ei_encode_long(msgbuf,&index,keylen); ei_encode_long(msgbuf,&index,obj->attr); ei_encode_long(msgbuf,&index,obj->size); switch (ei_reg_typeof(obj)) { case EI_INT: ei_encode_long(msgbuf,&index,obj->val.i); break; case EI_FLT: ei_encode_double(msgbuf,&index,obj->val.f); break; case EI_STR: if (obj->size > 0) ei_encode_string(msgbuf,&index,obj->val.s); else ei_encode_long(msgbuf,&index, (long)NULL); /* just the NULL pointer */ break; case EI_BIN: if (obj->size > 0) ei_encode_binary(msgbuf,&index,obj->val.p,obj->size); else ei_encode_long(msgbuf,&index,(long)(obj->val.p)); /* just the pointer */ break; default: if (dbuf) free(dbuf); return -1; } /* send it */ if (ei_send_encoded(fd,mnesia,msgbuf,index)) { if (dbuf) free(dbuf); return -1; } if (dbuf) free(dbuf); return 0; }
/*! * Return statistics (amount of calls) for the driver and the currently active * thread. This function does not send any arguments to the driver. * * \param[in] req Request * \param[in,out] res Result * \param[in,out] drv_state Driver state * \param[in,out] trd_state Thread state */ static void handle_stats(gd_req_t *req, gd_res_t *res, gdt_drv_t *drv, gdt_trd_t *trd) { /* Encode return tuple header */ ei_encode_tuple_header(res->buf, &res->index, 2); ei_encode_atom(res->buf, &res->index, "ok"); /* Encode proplist header */ ei_encode_list_header(res->buf, &res->index, 2); /* Encode driver calls */ ei_encode_tuple_header(res->buf, &res->index, 2); ei_encode_atom(res->buf, &res->index, "driver"); ei_encode_long(res->buf, &res->index, drv->count++); /* Encode thread calls */ ei_encode_tuple_header(res->buf, &res->index, 2); ei_encode_atom(res->buf, &res->index, "thread"); ei_encode_long(res->buf, &res->index, trd->count++); /* Encode empty list at the end */ ei_encode_empty_list(res->buf, &res->index); }
/* Called when uart_read completes or fails */ static void handle_read_completed(int rc, const uint8_t *data, size_t len) { if (rc >= 0) { char *resp = malloc(32 + len); int resp_index = sizeof(uint16_t); ei_encode_version(resp, &resp_index); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "ok"); ei_encode_binary(resp, &resp_index, data, len); erlcmd_send(resp, resp_index); free(resp); } else send_error_response(uart_last_error()); }
static int mn_send_commit(int fd, erlang_pid *mnesia, erlang_pid *self) { char buf[EISMALLBUF]; char *bufp=buf; char string[256]; int index = 0; int version,arity; int msglen; erlang_msg msg; int i; /* set up commit message { commit, self() } */ ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_atom(buf,&index,EI_MNESIA_COMMIT); ei_encode_pid(buf,&index,self); /* send it */ if (ei_send_encoded(fd,mnesia,buf,index)) return -1; /* get reply */ while (1) { index = EISMALLBUF; if (!(i=ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) continue; else if (i < 0) return -1; else break; } if (i == ERL_SEND) { index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || ei_decode_atom(buf,&index,string)) return -1; if (!strcmp(string,"ok")) return 0; } /* wrong message type */ return -1; }
static ErlDrvSSizeT syslogdrv_call(ErlDrvData handle, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int* flags) { syslogdrv_t* d = (syslogdrv_t*)handle; int index = 0; if (command != SYSLOGDRV_CLOSE) { return (ErlDrvSSizeT)ERL_DRV_ERROR_BADARG; } if (d->ident) { driver_free(d->ident); } d->ident = NULL; d->open = 0; if (ei_encode_version(*rbuf, &index) || ei_encode_atom(*rbuf, &index, "ok")) { return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL; } return index+1; }
/*! * Take an arbitrary mixed list of integers and floats and add all numbers, * returning the resulting sum. * * \param[in] req Request * \param[in,out] res Result * \param[in,out] drv_state Driver state * \param[in,out] trd_state Thread state */ static void handle_sum(gd_req_t *req, gd_res_t *res, gdt_drv_t *drv, gdt_trd_t *trd) { int type, size; /* Determine type and size */ if (ei_get_type(req->buf, &req->index, &type, &size) || size <= 0) return error_set(res, GDE_ERR_TYPE); /* Allocate memory for numbers */ double *values, sum = 0; if (!(values = driver_alloc(sizeof(double) * size))) return error_set(res, GDE_ERR_MEMORY); /* Decode list */ switch (type) { /* Decode integer list interpreted as string */ case ERL_STRING_EXT: { char value[size]; if (ei_decode_string(req->buf, &req->index, (char *)&value)) return error_set(res, GDE_ERR_DECODE); for (int v = 0; v < size; v++) values[v] = (double)value[v]; break; } /* Decode ordinary integer/double list */ case ERL_LIST_EXT: { if (ei_decode_list_header(req->buf, &req->index, &size)) return error_set(res, GDE_ERR_DECODE); for (int v = 0, temp; v < size; v++) { ei_get_type(req->buf, &req->index, &type, &temp); switch (type) { /* Decode integer */ case ERL_SMALL_INTEGER_EXT: case ERL_INTEGER_EXT: { long value; if (ei_decode_long(req->buf, &req->index, &value)) return error_set(res, GDE_ERR_DECODE); values[v] = (double)value; break; } /* Decode double */ case ERL_FLOAT_EXT: { double value; if (ei_decode_double(req->buf, &req->index, &value)) return error_set(res, GDE_ERR_DECODE); values[v] = (double)value; break; } /* Unsupported type */ default: return error_set(res, GDE_ERR_TYPE); } } /* A list always contains an empty list at the end */ if (ei_decode_list_header(req->buf, &req->index, NULL)) return error_set(res, GDE_ERR_DECODE); break; } /* Unsupported type */ default: return error_set(res, GDE_ERR_TYPE); } /* Sum up values */ for (int v = 0; v < size; v++) sum += values[v]; /* Free allocated memory */ driver_free(values); /* Encode resulting sum and return tuple */ ei_encode_tuple_header(res->buf, &res->index, 2); ei_encode_atom(res->buf, &res->index, "ok"); ei_encode_double(res->buf, &res->index, sum); /* Update counters */ drv->count++; trd->count++; }
static int mn_start_restore(int fd, const erlang_pid *self, erlang_pid *mnesia, const char *mntab, long *count, long *maxkey,long *maxobj) { char buf[EISMALLBUF]; char *bufp=buf; char tmpbuf[64]; int index = 0; erlang_msg msg; int arity; int version; int i; int needlink; int needmsg; int msglen; /* set up rpc arguments */ /* { PidFrom, { call, Mod, Fun, Args, user }} */ ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,EI_MNESIA_MODULE); /* Mod */ ei_encode_atom(buf,&index,EI_MNESIA_RESTORE); /* Fun */ ei_encode_list_header(buf,&index,2); /* Args: [ table, self() ] */ ei_encode_atom(buf,&index,mntab); ei_encode_pid(buf,&index,self); ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect link and message (not sure which will come first though) */ needlink = needmsg = 1; while (needlink || needmsg) { /* get message */ index = EISMALLBUF; while (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) index = EISMALLBUF; switch (i) { case ERL_LINK: /* got link */ if (!needlink) return -1; needlink = 0; break; case ERL_SEND: /* got message - is it the right one? */ if (!needmsg) return -1; else { /* expecting { rex, { size, Pid, Count, MaxKey, MaxObj }} */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_decode_tuple_header(buf,&index,&arity) || (arity != 5) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,EI_MNESIA_SIZE) || ei_decode_pid(buf,&index,mnesia) || ei_decode_long(buf,&index,count) || ei_decode_long(buf,&index,maxkey) || ei_decode_long(buf,&index,maxobj)) return -1; /* bad response from other side */ /* got msg */ needmsg = 0; } break; default: return -1; /* wasn't link or pid */ } } return 0; }
int ei_reg_restore(int fd, ei_reg *reg, const char *mntab) { int i,j; char tag[32]; char sbuf[EISMALLBUF]; char *dbuf = NULL; char *msgbuf = NULL; char *keybuf = NULL; erlang_pid self; erlang_pid mnesia = {"",0,0,0}; erlang_msg msg; int index = 0; int len = 0; int msglen; int version = 0; int arity = 0; long count = 0; long maxkey = 0; long maxobj = 0; ei_cnode *ec; if (!reg || !mntab) return -1; /* return EI_BADARG; */ /* make a self pid */ if ((ec = ei_fd_to_cnode(fd)) == NULL) { return -1; } strcpy(self.node,ei_thisnodename(ec)); self.num = fd; self.serial = 0; self.creation = ei_thiscreation(ec); if (mn_start_restore(fd,&self,&mnesia,mntab,&count,&maxkey,&maxobj)) { /* send exit *only* if we have pid */ if (mnesia.node[0]) ei_send_exit(fd,&self,&mnesia,"bad response from rpc start"); return -1; } if (count <= 0) { ei_send_exit(fd,&self,&mnesia,"nothing to do"); return 0; } /* make sure receive buffer can handle largest expected message */ len = maxkey + maxobj + 512; if (len > EISMALLBUF) if (!(dbuf = malloc(len))) { ei_send_exit(fd,&self,&mnesia,"cannot allocate space for incoming data"); return -1; } msgbuf = (dbuf ? dbuf : sbuf); /* allocate space for largest key */ if (!(keybuf = malloc(maxkey+1))) goto restore_failure; /* get this ball rolling */ index = 0; ei_encode_version(msgbuf,&index); ei_encode_tuple_header(msgbuf,&index,2); ei_encode_atom(msgbuf,&index,"send_records"); ei_encode_pid(msgbuf,&index,&self); if (ei_send_encoded(fd,&mnesia,msgbuf,index)) goto restore_failure; /* read as much as possible, until count or EXIT */ for (i=0; i<count; i++) { index = len; while ((j = ei_recv_internal(fd,&msgbuf,&index,&msg,&msglen,1,0)) == 0) index = len; if (j<0) goto restore_failure; /* decode the first part of the message */ index = 0; if ((msg.msgtype != ERL_SEND) || ei_decode_version(msgbuf,&index,&version) || ei_decode_tuple_header(msgbuf,&index,&arity) || (arity != 6) || ei_decode_atom(msgbuf,&index,tag) || strcmp(tag,EI_MNESIA_RECV)) goto restore_failure; /* decode the rest of the message and insert data into table */ if (mn_decode_insert(reg,msgbuf,&index,keybuf)) goto restore_failure; } if (keybuf) free(keybuf); if (dbuf) free(dbuf); /* wait for unlink */ if (mn_unlink(fd)) return -1; /* clear all the dirty bits */ ei_hash_foreach(reg->tab,clean_obj); /* success */ return 0; restore_failure: ei_send_exit(fd,&self,&mnesia,"restore failure"); if (keybuf) free(keybuf); if (dbuf) free(dbuf); return -1; }
static int mn_start_dump(int fd, const erlang_pid *self, erlang_pid *mnesia, const char *mntab) { char buf[EISMALLBUF]; char *bufp = buf; char tmpbuf[64]; int index = 0; erlang_msg msg; int type; int arity; int version; int msglen; int i; int needlink; int needpid; /* set up rpc arguments */ /* { PidFrom, { call, Mod, Fun, Args, user }} */ ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,EI_MNESIA_MODULE); /* Mod */ ei_encode_atom(buf,&index,EI_MNESIA_DUMP); /* Fun */ ei_encode_list_header(buf,&index,2); /* Args: [ table, self() ] */ ei_encode_atom(buf,&index,mntab); ei_encode_pid(buf,&index,self); ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect link and pid (not sure which will come first though) */ needlink = needpid = 1; while (needlink || needpid) { /* get message */ while (1) { index = EISMALLBUF; if (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) continue; else break; } switch (i) { case ERL_LINK: /* got link */ if (!needlink) return -1; needlink = 0; break; case ERL_SEND: /* got message - does it contain a pid? */ if (!needpid) return -1; else { /* expecting { rex, <pid> } */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_get_type_internal(buf,&index,&type,&arity) || (type != ERL_PID_EXT)) return -1; /* bad response from other side */ if (ei_decode_pid(buf,&index,mnesia)) return -1; /* got pid */ needpid = 0; } break; default: return -1; /* wasn't link or pid */ } } return 0; }
static ErlDrvSSizeT call(ErlDrvData edd, unsigned int cmd, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags) { dnssd_drv_t* dd = (dnssd_drv_t*) edd; int version, out_len, index, rindex, local_only; DNSServiceErrorType err; char* out_atom_text; ei_term arg, name, type, domain, txt, host, hostport; char *name_tmp, *type_tmp, *domain_tmp, *txt_tmp, *host_tmp; /* don't allow reuse */ if (dd->sd_ref) return -1; index = 0; dd->sd_ref = NULL; ei_decode_version(buf, &index, &version); ei_decode_ei_term(buf, &index, &arg); if (cmd == DNSSD_CMD_ENUM) { if (arg.ei_type == ERL_ATOM_EXT) { if (strncmp(arg.value.atom_name, "browse", 6) == 0) { // init for enum browse err = DNSServiceEnumerateDomains(&dd->sd_ref, kDNSServiceFlagsBrowseDomains, kDNSServiceInterfaceIndexAny, (DNSServiceDomainEnumReply) EnumReply, dd); } else if (strncmp(arg.value.atom_name,"reg", 3) == 0) { // init for enum reg err = DNSServiceEnumerateDomains(&dd->sd_ref, kDNSServiceFlagsRegistrationDomains, kDNSServiceInterfaceIndexAny, (DNSServiceDomainEnumReply) EnumReply, dd); } else { goto badarg; } } else { goto badarg; } } else if (cmd == DNSSD_CMD_BROWSE) { if (!arg.ei_type == ERL_TUPLE || arg.arity != 2) goto badarg; /* decode type */ ei_decode_ei_term(buf, &index, &type); if (type.ei_type != ERL_BINARY_EXT) goto badarg; index += 5; // skip tag + 4 byte size type_tmp = (char*)driver_alloc(type.size + 1); memset(type_tmp, 0, type.size + 1); memcpy(type_tmp, buf + index, type.size); index += type.size; /* decode domain */ ei_decode_ei_term(buf, &index, &domain); if (domain.ei_type != ERL_BINARY_EXT) { driver_free(type_tmp); goto badarg; } index += 5; // skip tag + 4 byte size domain_tmp = (char *) driver_alloc(domain.size + 1); memset(domain_tmp, 0, domain.size + 1); memcpy(domain_tmp, buf + index, domain.size); err = DNSServiceBrowse(&dd->sd_ref, 0, // Flags kDNSServiceInterfaceIndexAny, type_tmp, domain_tmp, (DNSServiceBrowseReply) BrowseReply, dd); driver_free(type_tmp); driver_free(domain_tmp); } else if (cmd == DNSSD_CMD_RESOLVE) { if (!arg.ei_type == ERL_TUPLE || arg.arity != 3) goto badarg; /* decode name */ ei_decode_ei_term(buf, &index, &name); if (name.ei_type != ERL_BINARY_EXT) goto badarg; index += 5; // skip tag + 4 byte size name_tmp = (char *) driver_alloc(name.size + 1); memset(name_tmp, 0, name.size + 1); memcpy(name_tmp, buf + index, name.size); index += name.size; /* decode type */ ei_decode_ei_term(buf, &index, &type); if (type.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); goto badarg; } index += 5; // skip tag + 4 byte size type_tmp = (char *) driver_alloc(type.size + 1); memset(type_tmp, 0, type.size + 1); memcpy(type_tmp, buf + index, type.size); index += type.size; /* decode domain */ ei_decode_ei_term(buf, &index, &domain); if (domain.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); driver_free(type_tmp); goto badarg; } index += 5; // skip tag + 4 byte size domain_tmp = (char *) driver_alloc(domain.size + 1); memset(domain_tmp, 0, domain.size + 1); memcpy(domain_tmp, buf + index, domain.size); /* start op */ err = DNSServiceResolve(&dd->sd_ref, 0, // Flags kDNSServiceInterfaceIndexAny, name_tmp, type_tmp, domain_tmp, (DNSServiceResolveReply) ResolveReply, dd); driver_free(name_tmp); driver_free(type_tmp); driver_free(domain_tmp); } else if (cmd == DNSSD_CMD_REGISTER) { if (!arg.ei_type == ERL_TUPLE || arg.arity != 6) goto badarg; /* decode name */ ei_decode_ei_term(buf, &index, &name); if (name.ei_type != ERL_BINARY_EXT) goto badarg; index += 5; // skip tag + 4 byte size name_tmp = (char *) driver_alloc(name.size + 1); memset(name_tmp, 0, name.size + 1); memcpy(name_tmp, buf + index, name.size); index += name.size; /* decode type */ ei_decode_ei_term(buf, &index, &type); if (type.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); goto badarg; } index += 5; // skip tag + 4 byte size type_tmp = (char *) driver_alloc(type.size + 1); memset(type_tmp, 0, type.size + 1); memcpy(type_tmp, buf + index, type.size); index += type.size; /* decode domain */ ei_decode_ei_term(buf, &index, &domain); if (domain.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); driver_free(type_tmp); goto badarg; } index += 5; // skip tag + 4 byte size domain_tmp = (char *) driver_alloc(domain.size + 1); memset(domain_tmp, 0, domain.size + 1); memcpy(domain_tmp, buf + index, domain.size); index += domain.size; /* decode host */ ei_decode_ei_term(buf, &index, &host); if (host.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); driver_free(type_tmp); driver_free(domain_tmp); goto badarg; } index += 5; // skip tag + 4 byte size host_tmp = (char *) driver_alloc(host.size + 1); memset(host_tmp, 0, host.size + 1); memcpy(host_tmp, buf + index, host.size); index += host.size; /* decode port */ ei_decode_ei_term(buf, &index, &hostport); if (hostport.ei_type != ERL_INTEGER_EXT && hostport.ei_type != ERL_SMALL_INTEGER_EXT) { driver_free(name_tmp); driver_free(type_tmp); driver_free(domain_tmp); driver_free(host_tmp); goto badarg; } /* decode txt */ ei_decode_ei_term(buf, &index, &txt); if (txt.ei_type != ERL_BINARY_EXT) { driver_free(name_tmp); driver_free(type_tmp); driver_free(domain_tmp); driver_free(host_tmp); goto badarg; } index += 5; // skip tag + 4 byte size txt_tmp = (char *) driver_alloc(txt.size + 1); memset(txt_tmp, 0, txt.size + 1); memcpy(txt_tmp, buf + index, txt.size); local_only = (0 == strcmp("localhost", host_tmp)); err = DNSServiceRegister(&dd->sd_ref, 0, // Flags local_only ? -1 : 0, // Interface: local / any name_tmp, type_tmp, local_only ? "local" : domain_tmp, host_tmp, htons(hostport.value.i_val), txt.size, txt_tmp, (DNSServiceRegisterReply) RegisterReply, dd); driver_free(name_tmp); driver_free(type_tmp); driver_free(domain_tmp); driver_free(host_tmp); driver_free(txt_tmp); } else { goto badarg; } rindex = 0; out_len = 0; ei_encode_version(NULL, &out_len); if (err == kDNSServiceErr_NoError) { #ifdef __WIN32__ dd->event = WSACreateEvent(); WSAEventSelect(DNSServiceRefSockFD(dd->sd_ref), dd->event, FD_READ); driver_select(dd->erl_port, dd->event, ERL_DRV_READ, 1); #else driver_select(dd->erl_port, (ErlDrvEvent)(size_t) DNSServiceRefSockFD(dd->sd_ref), ERL_DRV_READ, 1); #endif out_atom_text = "ok"; ei_encode_atom(NULL, &out_len, out_atom_text); if(rlen < out_len) { *rbuf = driver_alloc(out_len); rlen = out_len; } ei_encode_version(*rbuf, &rindex); ei_encode_atom(*rbuf, &rindex, out_atom_text); return out_len; } else { out_atom_text = "error"; ei_encode_tuple_header(NULL, &out_len, 2); ei_encode_atom(NULL, &out_len, out_atom_text); ei_encode_long(NULL, &out_len, 1337); if(rlen < out_len) { *rbuf = driver_alloc(out_len); rlen = out_len; } ei_encode_version(*rbuf, &rindex); ei_encode_tuple_header(*rbuf, &rindex, 2); ei_encode_atom(*rbuf, &rindex, out_atom_text); ei_encode_long(*rbuf, &rindex, (long) err); return out_len; } badarg: return -1; }
static void handle_configuration(const char *req, int *req_index) { char resp[256]; int resp_index = sizeof(uint16_t); // Space for payload size resp[resp_index++] = response_id; ei_encode_version(resp, &resp_index); ei_encode_list_header(resp, &resp_index, 5); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "speed"); ei_encode_long(resp, &resp_index, current_config.speed); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "data_bits"); ei_encode_long(resp, &resp_index, current_config.data_bits); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "stop_bits"); ei_encode_long(resp, &resp_index, current_config.stop_bits); ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "parity"); switch (current_config.parity) { default: case UART_PARITY_NONE: ei_encode_atom(resp, &resp_index, "none"); break; case UART_PARITY_EVEN: ei_encode_atom(resp, &resp_index, "even"); break; case UART_PARITY_ODD: ei_encode_atom(resp, &resp_index, "odd"); break; case UART_PARITY_SPACE: ei_encode_atom(resp, &resp_index, "space"); break; case UART_PARITY_MARK: ei_encode_atom(resp, &resp_index, "mark"); break; case UART_PARITY_IGNORE: ei_encode_atom(resp, &resp_index, "ignore"); break; } ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "flow_control"); switch (current_config.parity) { default: case UART_FLOWCONTROL_NONE: ei_encode_atom(resp, &resp_index, "none"); break; case UART_FLOWCONTROL_HARDWARE: ei_encode_atom(resp, &resp_index, "hardware"); break; case UART_FLOWCONTROL_SOFTWARE: ei_encode_atom(resp, &resp_index, "software"); break; } ei_encode_empty_list(resp, &resp_index); erlcmd_send(resp, resp_index); }
int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, char *msg, int msglen, unsigned ms) { char *s, header[1400]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ int err; ei_socket_callbacks *cbs; void *ctx; ssize_t len, tot_len; unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms; err = EI_GET_CBS_CTX__(&cbs, &ctx, fd); if (err) { EI_CONN_SAVE_ERRNO__(err); return ERL_ERROR; } /* are we tracing? */ /* check that he can receive trace tokens first */ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL); /* header = REG_SEND, from, cookie, toname max sizes: */ ei_encode_version(header,&index); /* 1 */ if (token) { ei_encode_tuple_header(header,&index,5); /* 2 */ ei_encode_long(header,&index,ERL_REG_SEND_TT); /* 2 */ } else { ei_encode_tuple_header(header,&index,4); ei_encode_long(header,&index,ERL_REG_SEND); } ei_encode_pid(header, &index, from); /* 268 */ ei_encode_atom(header, &index, ei_getfdcookie(fd)); /* 258 */ ei_encode_atom(header, &index, to); /* 268 */ if (token) ei_encode_trace(header,&index,token); /* 534 */ /* control message (precedes header actually) */ /* length = 1 ('p') + header len + message len */ s = header; put32be(s, index + msglen - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1336 */ if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); #ifdef EI_HAVE_STRUCT_IOVEC__ if (ei_socket_callbacks_have_writev__(cbs)) { struct iovec v[2]; v[0].iov_base = (char *)header; v[0].iov_len = index; v[1].iov_base = (char *)msg; v[1].iov_len = msglen; len = tot_len = (ssize_t) index+msglen; err = ei_writev_fill_ctx_t__(cbs, ctx, v, 2, &len, tmo); if (!err && len != tot_len) err = EIO; if (err) { EI_CONN_SAVE_ERRNO__(err); return -1; } return 0; } #endif /* EI_HAVE_STRUCT_IOVEC__ */ /* no writev() */ len = tot_len = (ssize_t) index; err = ei_write_fill_ctx_t__(cbs, ctx, header, &len, tmo); if (!err && len != tot_len) err = EIO; if (err) { EI_CONN_SAVE_ERRNO__(err); return -1; } len = tot_len = (ssize_t) msglen; err = ei_write_fill_ctx_t__(cbs, ctx, msg, &len, tmo); if (!err && len != tot_len) err = EIO; if (err) { EI_CONN_SAVE_ERRNO__(err); return -1; } return 0; }
/* This function is called from erlang:port_call/3. It works a lot like the control call-back, but uses the external term format for input and output. - command is an integer, obtained from the call from erlang (the second argument to erlang:port_call/3). - buf and len provide the arguments to the call (the third argument to erlang:port_call/3). They're decoded using ei. - rbuf points to a return buffer, rlen bytes long. The return data (written to *rbuf) should be a valid erlang term in the external term format. This is converted to an erlang term and returned by erlang:port_call/3 to the caller. If more space than rlen bytes is needed to return data, *rbuf can be set to memory allocated with driver_alloc. This memory will be freed automatically after call has returned. The return value (of this callback function) is the number of bytes returned in *rbuf. If ERL_DRV_ERROR_GENERAL is returned (or in fact, anything < 0), erlang:port_call/3 will throw a BAD_ARG. THIS IMPLEMENTATION of the callback handles two kinds of commands, INIT_COMMAND and ENGINE_COMMAND. An INIT_COMMAND should only be issued once during the lifecycle of the driver, *before* any data is sent to the port using port_command/port_control. The INIT_COMMAND causes the driver to load the specified shared library and call a predefined entry point (see the erlxsl_driver header file for details) to initialize an XslEngine structure. TODO: document ENGINE_COMMAND. TODO: locking during ENGINE_COMMAND calls TODO: support the transform command here as well - small binaries (which we can't/won't share/refcount) can be passed and copied... */ static int call(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen, unsigned int *flags) { int i; int type; int size; int index = 0; int rindex = 0; /*int arity; * char cmd[MAXATOMLEN];*/ char *data; DriverState state; DriverHandle *d = (DriverHandle*)drv_data; ei_decode_version(buf, &index, &i); if (command == INIT_COMMAND) { ei_get_type(buf, &index, &type, &size); INFO("ei_get_type %s of size = %i\n", ((char*)&type), size); // TODO: pull options tuple instead data = ALLOC(size + 1); ei_decode_string(buf, &index, data); INFO("Driver received data %s\n", data); state = init_provider(d, data); } else if (command == ENGINE_COMMAND) { DriverContext *ctx = ALLOC(sizeof(DriverContext)); // ErlDrvPort port = (ErlDrvPort)d->port; // XslEngine *engine = (XslEngine*)d->engine; // ErlDrvTermData callee_pid = driver_caller(port); Command *cmd = init_command(NULL, ctx, NULL, init_iov(Text, 0, NULL)); state = decode_ei_cmd(cmd, buf, &index); if (state == Success) { XslEngine *engine = (XslEngine*)d->engine; if (engine->command != NULL) { EngineState enstate = engine->command(cmd); if (enstate == Ok) { state = Success; } } } /*ei_get_type(buf, &index, &type, &size); INFO("ei_get_type %s of size = %i\n", ((char*)&type), size); data = ALLOC(size + 1); ei_decode_string(buf, &index, data);*/ } else { state = UnknownCommand; } ei_encode_version(*rbuf, &rindex); if (state == InitOk) { INFO("Provider configured with library %s\n", d->loader->name); #ifdef _DRV_SASL_LOGGING // TODO: pull the logging_port and install it.... #endif ei_encode_atom(*rbuf, &rindex, "configured"); } else if (state == Success) { ei_encode_tuple_header(*rbuf, &rindex, 2); ei_encode_atom(*rbuf, &rindex, "ok"); if (state == OutOfMemory) { ei_encode_string(*rbuf, &rindex, heap_space_exhausted); } else if (state == UnknownCommand) { ei_encode_string(*rbuf, &rindex, unknown_command); } else { const char *err = (d->loader)->error_message; ei_encode_string_len(*rbuf, &rindex, err, strlen(err)); } } else { ei_encode_tuple_header(*rbuf, &rindex, 2); ei_encode_atom(*rbuf, &rindex, "error"); if (state == OutOfMemory) { ei_encode_string(*rbuf, &rindex, heap_space_exhausted); } else if (state == UnknownCommand) { ei_encode_string(*rbuf, &rindex, unknown_command); } else { const char *err = (d->loader)->error_message; ei_encode_string_len(*rbuf, &rindex, err, strlen(err)); } } DRV_FREE(data); return(rindex); };
static void i2c_handle_request(const char *req, void *cookie) { struct i2c_info *i2c = (struct i2c_info *) cookie; // Commands are of the form {Command, Arguments}: // { atom(), term() } int req_index = sizeof(uint16_t); if (ei_decode_version(req, &req_index, NULL) < 0) errx(EXIT_FAILURE, "Message version issue?"); int arity; if (ei_decode_tuple_header(req, &req_index, &arity) < 0 || arity != 2) errx(EXIT_FAILURE, "expecting {cmd, args} tuple"); char cmd[MAXATOMLEN]; if (ei_decode_atom(req, &req_index, cmd) < 0) errx(EXIT_FAILURE, "expecting command atom"); char resp[256]; int resp_index = sizeof(uint16_t); // Space for payload size ei_encode_version(resp, &resp_index); if (strcmp(cmd, "read") == 0) { long int len; if (ei_decode_long(req, &req_index, &len) < 0 || len < 1 || len > I2C_SMBUS_BLOCK_MAX) errx(EXIT_FAILURE, "read amount: min=1, max=%d", I2C_SMBUS_BLOCK_MAX); char data[I2C_SMBUS_BLOCK_MAX]; if (i2c_transfer(i2c, 0, 0, data, len)) ei_encode_binary(resp, &resp_index, data,len); else { ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "error"); ei_encode_atom(resp, &resp_index, "i2c_read_failed"); } } else if (strcmp(cmd, "write") == 0) { char data[I2C_SMBUS_BLOCK_MAX]; int len; int type; long llen; if (ei_get_type(req, &req_index, &type, &len) < 0 || type != ERL_BINARY_EXT || len < 1 || len > I2C_SMBUS_BLOCK_MAX || ei_decode_binary(req, &req_index, &data, &llen) < 0) errx(EXIT_FAILURE, "write: need a binary between 1 and %d bytes", I2C_SMBUS_BLOCK_MAX); if (i2c_transfer(i2c, data, len, 0, 0)) ei_encode_atom(resp, &resp_index, "ok"); else { ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "error"); ei_encode_atom(resp, &resp_index, "i2c_write_failed"); } } else if (strcmp(cmd, "wrrd") == 0) { char write_data[I2C_SMBUS_BLOCK_MAX]; char read_data[I2C_SMBUS_BLOCK_MAX]; int write_len; long int read_len; int type; long llen; if (ei_decode_tuple_header(req, &req_index, &arity) < 0 || arity != 2) errx(EXIT_FAILURE, "wrrd: expecting {write_data, read_count} tuple"); if (ei_get_type(req, &req_index, &type, &write_len) < 0 || type != ERL_BINARY_EXT || write_len < 1 || write_len > I2C_SMBUS_BLOCK_MAX || ei_decode_binary(req, &req_index, &write_data, &llen) < 0) errx(EXIT_FAILURE, "wrrd: need a binary between 1 and %d bytes", I2C_SMBUS_BLOCK_MAX); if (ei_decode_long(req, &req_index, &read_len) < 0 || read_len < 1 || read_len > I2C_SMBUS_BLOCK_MAX) errx(EXIT_FAILURE, "wrrd: read amount: min=1, max=%d", I2C_SMBUS_BLOCK_MAX); if (i2c_transfer(i2c, write_data, write_len, read_data, read_len)) ei_encode_binary(resp, &resp_index, read_data, read_len); else { ei_encode_tuple_header(resp, &resp_index, 2); ei_encode_atom(resp, &resp_index, "error"); ei_encode_atom(resp, &resp_index, "i2c_wrrd_failed"); } } else errx(EXIT_FAILURE, "unknown command: %s", cmd); debug("sending response: %d bytes", resp_index); erlcmd_send(resp, resp_index); }
int main(void) #endif { ErlConnect conp; Erl_IpAddr thisipaddr = (Erl_IpAddr)0; FILE *fp = (FILE *)0; char* charp = "foo"; double *doublep = NULL; double doublex = 0.0; ei_cnode xec; ei_reg *ei_regp = NULL; ei_term eterm; ei_x_buff eix; erlang_big *bigp = NULL; erlang_fun efun; erlang_msg *msgp = NULL; erlang_msg emsg; erlang_pid *pidp = NULL; erlang_pid epid; erlang_port eport; erlang_ref eref; erlang_trace etrace; int *intp = NULL; int intx = 0; long *longp = NULL; long longx = 0; short creation = 0; struct ei_reg_stat *ei_reg_statp = NULL; struct ei_reg_tabstat *ei_reg_tabstatp = NULL; struct hostent *hostp = NULL; unsigned char * ucharp = (unsigned char *)"foo"; unsigned long *ulongp = NULL; unsigned long ulongx = 0; void *voidp = NULL; #ifndef VXWORKS EI_LONGLONG *longlongp = (EI_LONGLONG*)NULL; EI_LONGLONG longlongx = 0; EI_ULONGLONG *ulonglongp = (EI_ULONGLONG*)NULL; EI_ULONGLONG ulonglongx = 0; #endif enum erlang_char_encoding enc; intx = erl_errno; ei_connect_init(&xec, charp, charp, creation); ei_connect_xinit (&xec, charp, charp, charp, thisipaddr, charp, creation); ei_connect(&xec, charp); ei_xconnect (&xec, thisipaddr, charp); ei_receive(intx, ucharp, intx); ei_receive_msg(intx, &emsg, &eix); ei_xreceive_msg(intx, &emsg, &eix); ei_send(intx, &epid, charp, intx); ei_reg_send(&xec, intx, charp, charp, intx); ei_rpc(&xec, intx, charp, charp, charp, intx, &eix); ei_rpc_to(&xec, intx, charp, charp, charp, intx); ei_rpc_from(&xec, intx, intx, &emsg, &eix); ei_publish(&xec, intx); ei_accept(&xec, intx, &conp); ei_unpublish(&xec); ei_thisnodename(&xec); ei_thishostname(&xec); ei_thisalivename(&xec); ei_self(&xec); ei_gethostbyname(charp); ei_gethostbyaddr(charp, intx, intx); ei_gethostbyname_r(charp, hostp, charp, intx, intp); ei_gethostbyaddr_r(charp, intx, intx, hostp, charp, intx, intp); ei_encode_version(charp, intp); ei_x_encode_version(&eix); ei_encode_long(charp, intp, longx); ei_x_encode_long(&eix, longx); ei_encode_ulong(charp, intp, ulongx); ei_x_encode_ulong(&eix, ulongx); ei_encode_double(charp, intp, doublex); ei_x_encode_double(&eix, doublex); ei_encode_boolean(charp, intp, intx); ei_x_encode_boolean(&eix, intx); ei_encode_char(charp, intp, 'a'); ei_x_encode_char(&eix, 'a'); ei_encode_string(charp, intp, charp); ei_encode_string_len(charp, intp, charp, intx); ei_x_encode_string(&eix, charp); ei_x_encode_string_len(&eix, charp, intx); ei_encode_atom(charp, intp, charp); ei_encode_atom_as(charp, intp, charp, ERLANG_LATIN1, ERLANG_UTF8); ei_encode_atom_len(charp, intp, charp, intx); ei_encode_atom_len_as(charp, intp, charp, intx, ERLANG_ASCII, ERLANG_LATIN1); ei_x_encode_atom(&eix, charp); ei_x_encode_atom_as(&eix, charp, ERLANG_LATIN1, ERLANG_UTF8); ei_x_encode_atom_len(&eix, charp, intx); ei_x_encode_atom_len_as(&eix, charp, intx, ERLANG_LATIN1, ERLANG_UTF8); ei_encode_binary(charp, intp, (void *)0, longx); ei_x_encode_binary(&eix, (void*)0, intx); ei_encode_pid(charp, intp, &epid); ei_x_encode_pid(&eix, &epid); ei_encode_fun(charp, intp, &efun); ei_x_encode_fun(&eix, &efun); ei_encode_port(charp, intp, &eport); ei_x_encode_port(&eix, &eport); ei_encode_ref(charp, intp, &eref); ei_x_encode_ref(&eix, &eref); ei_encode_trace(charp, intp, &etrace); ei_x_encode_trace(&eix, &etrace); ei_encode_tuple_header(charp, intp, intx); ei_x_encode_tuple_header(&eix, longx); ei_encode_list_header(charp, intp, intx); ei_x_encode_list_header(&eix, longx); /* #define ei_encode_empty_list(buf,i) ei_encode_list_header(buf,i,0) */ ei_x_encode_empty_list(&eix); ei_get_type(charp, intp, intp, intp); ei_get_type_internal(charp, intp, intp, intp); ei_decode_version(charp, intp, intp); ei_decode_long(charp, intp, longp); ei_decode_ulong(charp, intp, ulongp); ei_decode_double(charp, intp, doublep); ei_decode_boolean(charp, intp, intp); ei_decode_char(charp, intp, charp); ei_decode_string(charp, intp, charp); ei_decode_atom(charp, intp, charp); ei_decode_atom_as(charp, intp, charp, MAXATOMLEN_UTF8, ERLANG_WHATEVER, &enc, &enc); ei_decode_binary(charp, intp, (void *)0, longp); ei_decode_fun(charp, intp, &efun); free_fun(&efun); ei_decode_pid(charp, intp, &epid); ei_decode_port(charp, intp, &eport); ei_decode_ref(charp, intp, &eref); ei_decode_trace(charp, intp, &etrace); ei_decode_tuple_header(charp, intp, intp); ei_decode_list_header(charp, intp, intp); ei_decode_ei_term(charp, intp, &eterm); ei_print_term(fp, charp, intp); ei_s_print_term(&charp, charp, intp); ei_x_format(&eix, charp); ei_x_format_wo_ver(&eix, charp); ei_x_new(&eix); ei_x_new_with_version(&eix); ei_x_free(&eix); ei_x_append(&eix, &eix); ei_x_append_buf(&eix, charp, intx); ei_skip_term(charp, intp); ei_reg_open(intx); ei_reg_resize(ei_regp, intx); ei_reg_close(ei_regp); ei_reg_setival(ei_regp, charp, longx); ei_reg_setfval(ei_regp, charp, doublex); ei_reg_setsval(ei_regp, charp, charp); ei_reg_setpval(ei_regp, charp, voidp, intx); ei_reg_setval(ei_regp, charp, intx); ei_reg_getival(ei_regp, charp); ei_reg_getfval(ei_regp, charp); ei_reg_getsval(ei_regp, charp); ei_reg_getpval(ei_regp, charp, intp); ei_reg_getval(ei_regp, charp, intx); ei_reg_markdirty(ei_regp, charp); ei_reg_delete(ei_regp, charp); ei_reg_stat(ei_regp, charp, ei_reg_statp); ei_reg_tabstat(ei_regp, ei_reg_tabstatp); ei_reg_dump(intx, ei_regp, charp, intx); ei_reg_restore(intx, ei_regp, charp); ei_reg_purge(ei_regp); #if defined(HAVE_GMP_H) && defined(HAVE_LIBGMP) { mpz_t obj; ei_decode_bignum(charp, intp, obj); ei_encode_bignum(charp, intp, obj); ei_x_encode_bignum(&eix, obj); } #endif /* HAVE_GMP_H && HAVE_LIBGMP */ #ifndef VXWORKS ei_decode_longlong(charp, intp, longlongp); ei_decode_ulonglong(charp, intp, ulonglongp); ei_encode_longlong(charp, intp, longlongx); ei_encode_ulonglong(charp, intp, ulonglongx); ei_x_encode_longlong(&eix, longlongx); ei_x_encode_ulonglong(&eix, ulonglongx); #endif #ifdef USE_EI_UNDOCUMENTED ei_decode_intlist(charp, intp, longp, intp); ei_receive_encoded(intx, &charp, intp, msgp, intp); ei_send_encoded(intx, pidp, charp, intx); ei_send_reg_encoded(intx, pidp, charp, charp, intx); ei_decode_big(charp, intp, bigp); ei_big_comp(bigp, bigp); ei_big_to_double(bigp, doublep); ei_small_to_big(intx, bigp); ei_alloc_big(intx); ei_free_big(bigp); #endif /* USE_EI_UNDOCUMENTED */ return BUFSIZ + EAGAIN + EHOSTUNREACH + EIO + EI_BIN + EI_DELET + EI_DIRTY + EI_FLT + EI_FORCE + EI_INT + EI_NOPURGE + EI_STR + EMSGSIZE + ENOMEM + ERL_ERROR + ERL_EXIT + ERL_LINK + ERL_MSG + ERL_NO_TIMEOUT + ERL_REG_SEND + ERL_SEND + ERL_TICK + ERL_TIMEOUT + ERL_UNLINK + ETIMEDOUT + MAXATOMLEN; }
/* global:unregister_name(name) -> ok */ int erl_global_unregister(int fd, const char *name) { char buf[EISMALLBUF]; char *bufp=buf; char tmpbuf[64]; int index = 0; erlang_pid *self = erl_self(); erlang_msg msg; int i; int version,arity,msglen; int needunlink, needatom, needdemonitor; /* make a self pid */ self->num = fd; ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,"global"); /* Mod */ ei_encode_atom(buf,&index,"unregister_name_external"); /* Fun */ ei_encode_list_header(buf,&index,1); /* Args: [ name ] */ ei_encode_atom(buf,&index,name); ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect unlink and an atom, or just an atom */ needunlink = needatom = needdemonitor = 1; while (1) { /* get message */ while (1) { index = EISMALLBUF; if (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) continue; else break; } switch (i) { case ERL_UNLINK: /* got unlink */ if (!needunlink) return -1; needunlink = 0; break; case ERL_DEMONITOR_P-10: /* got demonitor */ if (!needdemonitor) return -1; needdemonitor = 0; break; case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; else { /* expecting { rex, ok } */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"ok")) return -1; /* bad response from other side */ /* we're done here */ return 0; } break; default: return -1; } } return 0; }
int ei_send_encoded_tmo(int fd, const erlang_pid *to, char *msg, int msglen, unsigned ms) { char *s, header[1200]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ int res; #ifdef HAVE_WRITEV struct iovec v[2]; #endif /* are we tracing? */ /* check that he can receive trace tokens first */ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL); /* header = SEND, cookie, to max sizes: */ ei_encode_version(header,&index); /* 1 */ if (token) { ei_encode_tuple_header(header,&index,4); /* 2 */ ei_encode_long(header,&index,ERL_SEND_TT); /* 2 */ } else { ei_encode_tuple_header(header,&index,3); ei_encode_long(header,&index,ERL_SEND); } ei_encode_atom(header,&index,ei_getfdcookie(fd)); /* 258 */ ei_encode_pid(header,&index,to); /* 268 */ if (token) ei_encode_trace(header,&index,token); /* 534 */ /* control message (precedes header actually) */ /* length = 1 ('p') + header len + message len */ s = header; put32be(s, index + msglen - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1070 */ if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); #ifdef HAVE_WRITEV v[0].iov_base = (char *)header; v[0].iov_len = index; v[1].iov_base = (char *)msg; v[1].iov_len = msglen; if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } #else /* !HAVE_WRITEV */ if ((res = ei_write_fill_t(fd,header,index,ms)) != index) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } #endif /* !HAVE_WRITEV */ return 0; }
static void encode_kv_bool(char *resp, int *resp_index, const char *key, int value) { ei_encode_atom(resp, resp_index, key); ei_encode_boolean(resp, resp_index, value); }
int erl_global_register(int fd, const char *name, ETERM *pid) { char buf[EISMALLBUF]; char *bufp=buf; char tmpbuf[64]; int index = 0; erlang_pid self; erlang_msg msg; int needlink, needatom, needmonitor; int arity; int version; int msglen; int i; /* get that pid into a better format */ if (!erl_encode(pid,(unsigned char*)buf)) return -1; if (ei_decode_version(buf,&index,&version) || ei_decode_pid(buf,&index,&self)) return -1; /* set up rpc arguments */ /* { PidFrom, { call, Mod, Fun, Args, user }} */ index = 0; ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,&self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,"global"); /* Mod */ ei_encode_atom(buf,&index,"register_name_external"); /* Fun */ ei_encode_list_header(buf,&index,3); /* Args: [ name, self(), cnode ] */ ei_encode_atom(buf,&index,name); ei_encode_pid(buf,&index,&self); ei_encode_tuple_header(buf,&index,2); ei_encode_atom(buf,&index,"global"); /* special "resolve" treatment */ ei_encode_atom(buf,&index,"cnode"); /* i.e. we get a SEND when conflict */ ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1; /* get the reply: expect link and an atom, or just an atom */ needlink = needatom = needmonitor = 1; while (1) { /* get message */ while (1) { index = EISMALLBUF; if (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) continue; else break; } switch (i) { case ERL_LINK: /* got link */ if (!needlink) return -1; needlink = 0; break; case ERL_MONITOR_P-10: /* got monitor */ if (!needmonitor) { return -1;} needmonitor = 0; break; case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; else { /* expecting { rex, yes } */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"yes")) return -1; /* bad response from other side */ /* we're done */ return 0; } break; default: return -1; /* something else */ } } return 0; }
int32_t spawn(char protocol, uint32_t * ports, uint32_t ports_len, char * filename, uint32_t /*filename_len*/, char * argv, uint32_t argv_len, char * env, uint32_t env_len) { int type; if (protocol == 't') // tcp { type = SOCK_STREAM; } else if (protocol == 'u') // udp { type = SOCK_DGRAM; } else { return spawn_status::invalid_input; } int fds_stdout[2] = {-1, -1}; int fds_stderr[2] = {-1, -1}; if (::pipe(fds_stdout) == -1) return spawn_status::errno_pipe(); if (::pipe(fds_stderr) == -1) return spawn_status::errno_pipe(); pid_t const pid = fork(); if (pid == -1) { return spawn_status::errno_fork(); } else if (pid == 0) { for (size_t i = 0; i < GEPD::nfds; ++i) { if (::close(GEPD::fds[i].fd) == -1) ::_exit(spawn_status::errno_close()); } if (::dup2(fds_stdout[1], 1) == -1) ::_exit(spawn_status::errno_dup()); if (::close(fds_stdout[0]) == -1 || close(fds_stdout[1]) == -1) ::_exit(spawn_status::errno_close()); if (::dup2(fds_stderr[1], 2) == -1) ::_exit(spawn_status::errno_dup()); if (::close(fds_stderr[0]) == -1 || close(fds_stderr[1]) == -1) ::_exit(spawn_status::errno_close()); char pid_message[1024]; int pid_message_index = 0; unsigned long const pid_child = ::getpid(); if (ei_encode_version(pid_message, &pid_message_index)) ::_exit(GEPD::ExitStatus::ei_encode_error); if (ei_encode_tuple_header(pid_message, &pid_message_index, 2)) ::_exit(GEPD::ExitStatus::ei_encode_error); if (ei_encode_atom(pid_message, &pid_message_index, "pid")) ::_exit(GEPD::ExitStatus::ei_encode_error); if (ei_encode_ulong(pid_message, &pid_message_index, pid_child)) ::_exit(GEPD::ExitStatus::ei_encode_error); for (size_t i = 0; i < ports_len; ++i) { int sockfd = ::socket(AF_INET, type, 0); if (sockfd == -1) ::_exit(spawn_status::errno_socket()); if (type == SOCK_STREAM) { int tcp_nodelay_flag = 1; // set TCP_NODELAY to turn off Nagle's algorithm if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &tcp_nodelay_flag, sizeof(int)) == -1) ::_exit(spawn_status::socket_unknown); } if (static_cast<size_t>(sockfd) != i + 3) { if (::dup2(sockfd, i + 3) == -1) ::_exit(spawn_status::errno_dup()); sockfd = i + 3; } struct sockaddr_in localhost; localhost.sin_family = AF_INET; localhost.sin_port = htons(ports[i]); localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (::connect(sockfd, reinterpret_cast<struct sockaddr *>(&localhost), sizeof(localhost)) == -1) ::_exit(spawn_status::errno_connect()); if (i == 0) { // let the first connection get a pid message for attempting // to kill the OS process when the Erlang process terminates if (::write(sockfd, pid_message, pid_message_index) == -1) ::_exit(spawn_status::errno_write()); } } int argv_count = 2; { assert(argv[argv_len - 1] == '\0'); for (size_t i = 1; i < argv_len; ++i) { if (argv[i] == '\0') ++argv_count; } } char * execve_argv[argv_count]; { int index = 0; execve_argv[index++] = filename; if (argv_count == 2) { execve_argv[index++] = 0; } else { execve_argv[index++] = argv; for (size_t i = 0; i < argv_len - 1; ++i) { if (argv[i] == '\0') execve_argv[index++] = &(argv[i + 1]); } execve_argv[index++] = 0; assert(index == argv_count); } } int env_count = 1; { assert(env[env_len - 1] == '\0'); for (size_t i = 1; i < env_len; ++i) { if (env[i] == '\0') ++env_count; } } char * execve_env[env_count]; { if (env_count == 1) { execve_env[0] = 0; } else { int index = 0; execve_env[index++] = env; for (size_t i = 0; i < env_len - 1; ++i) { if (env[i] == '\0') execve_env[index++] = &(env[i + 1]); } execve_env[index++] = 0; assert(index == env_count); } } ::execve(filename, execve_argv, execve_env); ::_exit(spawn_status::errno_exec()); } else { if (::close(fds_stdout[1]) == -1) return spawn_status::errno_close(); if (::close(fds_stderr[1]) == -1) return spawn_status::errno_close(); if (GEPD::fds.reserve(GEPD::nfds + 2) == false) ::exit(spawn_status::out_of_memory); size_t const index_stdout = GEPD::nfds; size_t const index_stderr = GEPD::nfds + 1; GEPD::fds[index_stdout].fd = fds_stdout[0]; GEPD::fds[index_stdout].events = POLLIN | POLLPRI; GEPD::fds[index_stdout].revents = 0; GEPD::fds[index_stderr].fd = fds_stderr[0]; GEPD::fds[index_stderr].events = POLLIN | POLLPRI; GEPD::fds[index_stderr].revents = 0; GEPD::nfds += 2; copy_ptr<process_data> P(new process_data(pid, index_stdout, index_stderr)); processes.push_back(P); } return pid; }