static void parseline (char *b) { char *e = strchr (b, '='); if (!e) return; if (*b=='#') return; *e++ = 0; if (*e=='$') e = r_sys_getenv (e); if (e == NULL) return; if (!strcmp (b, "program")) _args[0] = _program = strdup (e); else if (!strcmp (b, "connect")) _connect = strdup (e); else if (!strcmp (b, "listen")) _listen = strdup (e); else if (!strcmp (b, "stdout")) _stdout = strdup (e); else if (!strcmp (b, "stdin")) _stdin = strdup (e); else if (!strcmp (b, "input")) _input = strdup (e); else if (!strcmp (b, "chdir")) _chgdir = strdup (e); else if (!strcmp (b, "chroot")) _chroot = strdup (e); else if (!strcmp (b, "preload")) _preload = strdup (e); else if (!strcmp (b, "setuid")) _setuid = strdup (e); else if (!strcmp (b, "seteuid")) _seteuid = strdup (e); else if (!strcmp (b, "setgid")) _setgid = strdup (e); else if (!strcmp (b, "setegid")) _setegid = strdup (e); else if (!memcmp (b, "arg", 3)) { int n = atoi (b+3); if (n>=0 && n<NARGS) { _args[n] = strdup (e); r_str_escape (_args[n]); } else eprintf ("Out of bounds args index: %d\n", n); } else if (!strcmp (b, "timeout")) _timeout = atoi (e); else if (!strcmp (b, "setenv")) { char *v = strchr (e, '='); if (v) { *v++ = 0; r_sys_setenv (e, v); } } }
/* Print out the JSON body for memory maps in the passed map region */ static void print_debug_map_json(RDebug *dbg, RDebugMap *map, bool prefix_comma) { dbg->cb_printf ("%s{", prefix_comma ? ",": ""); if (map->name && *map->name) { char *escaped_name = r_str_escape (map->name); dbg->cb_printf ("\"name\":\"%s\",", escaped_name); free (escaped_name); } if (map->file && *map->file) { char *escaped_path = r_str_escape (map->file); dbg->cb_printf ("\"file\":\"%s\",", escaped_path); free (escaped_path); } dbg->cb_printf ("\"addr\":%"PFMT64u",", map->addr); dbg->cb_printf ("\"addr_end\":%"PFMT64u",", map->addr_end); dbg->cb_printf ("\"type\":\"%c\",", map->user?'u':'s'); dbg->cb_printf ("\"perm\":\"%s\"", r_str_rwx_i (map->perm)); dbg->cb_printf ("}"); }
static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) { char *pstr, *str = r_str_escape (d->str); if (str) { if (d->type=='s' && !*str) { free (str); return; } if (d->type != 'C') { r_name_filter (str, 0); pstr = str; } else pstr = d->str; // r_str_sanitize (str); switch (rad) { case 'j': a->printf ("{\"offset\":%"PFMT64d", \"type\":\"%s\", \"name\":\"%s\"}", d->from, r_meta_type_to_string (d->type), str); break; case 0: a->printf ("0x%08"PFMT64x" %s\n", d->from, str); case 1: case '*': default: if (d->type == 'C') { a->printf ("\"%s %s\" @ 0x%08"PFMT64x"\n", r_meta_type_to_string (d->type), pstr, d->from); } else { a->printf ("%s %d 0x%08"PFMT64x" # %s\n", r_meta_type_to_string (d->type), d->size, d->from, pstr); } break; } free (str); } }
R_API int r_sys_cmd_str_full(const char *cmd, const char *input, char **output, int *len, char **sterr) { char buffer[1024], *outputptr = NULL; char *inputptr = (char *)input; int pid, bytes = 0, status; int sh_in[2], sh_out[2], sh_err[2]; if (len) *len = 0; if (pipe (sh_in)) return R_FALSE; if (output) { if (pipe (sh_out)) { close (sh_in[0]); close (sh_in[1]); close (sh_out[0]); close (sh_out[1]); return R_FALSE; } } if (pipe (sh_err)) { close (sh_in[0]); close (sh_in[1]); return R_FALSE; } switch ((pid=fork ())) { case -1: return R_FALSE; case 0: dup2 (sh_in[0], 0); close (sh_in[0]); close (sh_in[1]); if (output) { dup2 (sh_out[1], 1); close (sh_out[0]); close (sh_out[1]); } if (sterr) dup2 (sh_err[1], 2); else close (2); close (sh_err[0]); close (sh_err[1]); exit (r_sandbox_system (cmd, 0)); default: outputptr = strdup (""); if (!outputptr) return R_FALSE; if (sterr) { *sterr = strdup (""); if (!*sterr) { free (outputptr); return R_FALSE; } } if (output) close (sh_out[1]); close (sh_err[1]); close (sh_in[0]); if (!inputptr || !*inputptr) close (sh_in[1]); for (;;) { fd_set rfds, wfds; int nfd; FD_ZERO (&rfds); FD_ZERO (&wfds); if (output) FD_SET (sh_out[0], &rfds); if (sterr) FD_SET (sh_err[0], &rfds); if (inputptr && *inputptr) FD_SET (sh_in[1], &wfds); memset (buffer, 0, sizeof (buffer)); nfd = select (sh_err[0] + 1, &rfds, &wfds, NULL, NULL); if (nfd < 0) break; if (output && FD_ISSET (sh_out[0], &rfds)) { if ((bytes = read (sh_out[0], buffer, sizeof (buffer)-1)) == 0) break; buffer[sizeof(buffer) - 1] = '\0'; if (len) *len += bytes; outputptr = r_str_concat (outputptr, buffer); } else if (FD_ISSET (sh_err[0], &rfds) && sterr) { if (read (sh_err[0], buffer, sizeof (buffer)-1) == 0) break; buffer[sizeof(buffer) - 1] = '\0'; *sterr = r_str_concat (*sterr, buffer); } else if (FD_ISSET (sh_in[1], &wfds) && inputptr && *inputptr) { bytes = write (sh_in[1], inputptr, strlen (inputptr)); inputptr += bytes; if (!*inputptr) { close (sh_in[1]); /* If neither stdout nor stderr should be captured, * abort now - nothing more to do for select(). */ if (!output && !sterr) break; } } } if (output) close (sh_out[0]); close (sh_err[0]); close (sh_in[1]); waitpid (pid, &status, 0); if (status != 0) { char *escmd = r_str_escape (cmd); eprintf ("%s: failed command '%s'\n", __func__, escmd); free (escmd); return R_FALSE; } if (output) *output = outputptr; else free (outputptr); return R_TRUE; } return R_FALSE; }
/* TODO: simplify using r_write */ static int cmd_write(void *data, const char *input) { ut64 off; ut8 *buf; const char *arg; int wseek, i, size, len = strlen (input); char *tmp, *str, *ostr; RCore *core = (RCore *)data; #define WSEEK(x,y) if(wseek)r_core_seek_delta(x,y) wseek = r_config_get_i (core->config, "cfg.wseek"); str = ostr = strdup (input+1); switch (*input) { case 'p': if (input[1]==' ' && input[2]) { r_core_patch (core, input+2); } else { eprintf ("Usage: wp [rapatch-file]\n" "TODO: rapatch format documentation here\n"); } break; case 'r': off = r_num_math (core->num, input+1); len = (int)off; if (len>0) { buf = malloc (len); if (buf != NULL) { r_num_irand (); for (i=0; i<len; i++) buf[i] = r_num_rand (256); r_core_write_at (core, core->offset, buf, len); WSEEK (core, len); free (buf); } else eprintf ("Cannot allocate %d bytes\n", len); } break; case 'A': switch (input[1]) { case ' ': if (input[2] && input[3]==' ') { r_asm_set_pc (core->assembler, core->offset); eprintf ("modify (%c)=%s\n", input[2], input+4); len = r_asm_modify (core->assembler, core->block, input[2], r_num_math (core->num, input+4)); eprintf ("len=%d\n", len); if (len>0) { r_core_write_at (core, core->offset, core->block, len); WSEEK (core, len); } else eprintf ("r_asm_modify = %d\n", len); } else eprintf ("Usage: wA [type] [value]\n"); break; case '?': default: r_cons_printf ("Usage: wA [type] [value]\n" "Types:\n" " r raw write value\n" " v set value (taking care of current address)\n" " d destination register\n" " 0 1st src register\n" " 1 2nd src register\n" "Example: wA r 0 # e800000000\n"); break; } break; case 'c': switch (input[1]) { case 'i': r_io_cache_commit (core->io); r_core_block_read (core, 0); break; case 'r': r_io_cache_reset (core->io, R_TRUE); /* Before loading the core block we have to make sure that if * the cache wrote past the original EOF these changes are no * longer displayed. */ memset (core->block, 0xff, core->blocksize); r_core_block_read (core, 0); break; case '-': if (input[2]=='*') { r_io_cache_reset (core->io, R_TRUE); } else if (input[2]==' ') { char *p = strchr (input+3, ' '); ut64 to, from = core->offset; if (p) { *p = 0; from = r_num_math (core->num, input+3); to = r_num_math (core->num, input+3); if (to<from) { eprintf ("Invalid range (from>to)\n"); return 0; } } else { from = r_num_math (core->num, input+3); to = from + core->blocksize; } r_io_cache_invalidate (core->io, from, to); } else { eprintf ("Invalidate write cache at 0x%08"PFMT64x"\n", core->offset); r_io_cache_invalidate (core->io, core->offset, core->offset+core->blocksize); } /* See 'r' above. */ memset (core->block, 0xff, core->blocksize); r_core_block_read (core, 0); break; case '?': r_cons_printf ( "Usage: wc[ir*?]\n" " wc list all write changes\n" " wc- [a] [b] remove write op at curseek or given addr\n" " wc* \"\" in radare commands\n" " wcr reset all write changes in cache\n" " wci commit write cache\n" "NOTE: Requires 'e io.cache=true'\n"); break; case '*': r_io_cache_list (core->io, R_TRUE); break; case '\0': r_io_cache_list (core->io, R_FALSE); break; } break; case ' ': /* write string */ len = r_str_escape (str); r_core_write_at (core, core->offset, (const ut8*)str, len); #if 0 r_io_set_fd (core->io, core->file->fd); r_io_write_at (core->io, core->offset, (const ut8*)str, len); #endif WSEEK (core, len); r_core_block_read (core, 0); break; case 't': if (*str != ' ') { eprintf ("Usage: wt file [size]\n"); } else { tmp = strchr (str+1, ' '); if (tmp) { st64 sz = (st64) r_num_math (core->num, tmp+1); *tmp = 0; if (sz<1) eprintf ("Invalid length\n"); else r_core_dump (core, str+1, core->offset, (ut64)sz); } else r_file_dump (str+1, core->block, core->blocksize); } break; case 'T': eprintf ("TODO: wT // why?\n"); break; case 'f': arg = (const char *)(input+((input[1]==' ')?2:1)); if ((buf = (ut8*) r_file_slurp (arg, &size))) { r_io_set_fd (core->io, core->file->fd); r_io_write_at (core->io, core->offset, buf, size); WSEEK (core, size); free(buf); r_core_block_read (core, 0); } else eprintf ("Cannot open file '%s'\n", arg); break; case 'F': arg = (const char *)(input+((input[1]==' ')?2:1)); if ((buf = r_file_slurp_hexpairs (arg, &size))) { r_io_set_fd (core->io, core->file->fd); r_io_write_at (core->io, core->offset, buf, size); WSEEK (core, size); free (buf); r_core_block_read (core, 0); } else eprintf ("Cannot open file '%s'\n", arg); break; case 'w': str++; len = (len-1)<<1; if (len>0) tmp = malloc (len+1); else tmp = NULL; if (tmp) { for (i=0; i<len; i++) { if (i%2) tmp[i] = 0; else tmp[i] = str[i>>1]; } str = tmp; r_io_set_fd (core->io, core->file->fd); r_io_write_at (core->io, core->offset, (const ut8*)str, len); WSEEK (core, len); r_core_block_read (core, 0); free (tmp); } else eprintf ("Cannot malloc %d\n", len); break; case 'x': { int b, len = strlen (input); ut8 *buf = malloc (len+1); len = r_hex_str2bin (input+1, buf); if (len != 0) { if (len<0) len = -len+1; b = core->block[len]&0xf; b |= (buf[len]&0xf0); buf[len] = b; r_core_write_at (core, core->offset, buf, len); WSEEK (core, len); r_core_block_read (core, 0); } else eprintf ("Error: invalid hexpair string\n"); free (buf); } break; case 'a': switch (input[1]) { case 'o': if (input[2] == ' ') r_core_hack (core, input+3); else r_core_hack_help (core); break; case ' ': case '*': { const char *file = input[1]=='*'? input+2: input+1; RAsmCode *acode; r_asm_set_pc (core->assembler, core->offset); acode = r_asm_massemble (core->assembler, file); if (acode) { if (input[1]=='*') { r_cons_printf ("wx %s\n", acode->buf_hex); } else { if (r_config_get_i (core->config, "scr.prompt")) eprintf ("Written %d bytes (%s)=wx %s\n", acode->len, input+1, acode->buf_hex); r_core_write_at (core, core->offset, acode->buf, acode->len); WSEEK (core, acode->len); r_core_block_read (core, 0); } r_asm_code_free (acode); } } break; case 'f': if ((input[2]==' '||input[2]=='*')) { const char *file = input[2]=='*'? input+4: input+3; RAsmCode *acode; r_asm_set_pc (core->assembler, core->offset); acode = r_asm_assemble_file (core->assembler, file); if (acode) { if (input[2]=='*') { r_cons_printf ("wx %s\n", acode->buf_hex); } else { if (r_config_get_i (core->config, "scr.prompt")) eprintf ("Written %d bytes (%s)=wx %s\n", acode->len, input+1, acode->buf_hex); r_core_write_at (core, core->offset, acode->buf, acode->len); WSEEK (core, acode->len); r_core_block_read (core, 0); } r_asm_code_free (acode); } else eprintf ("Cannot assemble file\n"); } else eprintf ("Wrong argument\n"); break; default: eprintf ("Usage: wa[of*] [arg]\n" " wa nop : write nopcode using asm.arch and asm.bits\n" " wa* mov eax, 33 : show 'wx' op with hexpair bytes of sassembled opcode\n" " \"wa nop;nop\" : assemble more than one instruction (note the quotes)\n" " waf foo.asm : assemble file and write bytes\n" " wao nop : convert current opcode into nops\n" " wao? : show help for assembler operation on current opcode (hack)\n"); break; } break; case 'b': { int len = strlen (input); ut8 *buf = malloc (len+1); if (buf) { len = r_hex_str2bin (input+1, buf); if (len > 0) { r_mem_copyloop (core->block, buf, core->blocksize, len); r_core_write_at (core, core->offset, core->block, core->blocksize); WSEEK (core, core->blocksize); r_core_block_read (core, 0); } else eprintf ("Wrong argument\n"); } else eprintf ("Cannot malloc %d\n", len+1); } break; case 'm': size = r_hex_str2bin (input+1, (ut8*)str); switch (input[1]) { case '\0': eprintf ("Current write mask: TODO\n"); // TODO break; case '?': break; case '-': r_io_set_write_mask(core->io, 0, 0); eprintf ("Write mask disabled\n"); break; case ' ': if (size>0) { r_io_set_fd (core->io, core->file->fd); r_io_set_write_mask (core->io, (const ut8*)str, size); WSEEK (core, size); eprintf ("Write mask set to '"); for (i=0; i<size; i++) eprintf ("%02x", str[i]); eprintf ("'\n"); } else eprintf ("Invalid string\n"); break; } break; case 'v': { int type = 0; ut8 addr1; ut16 addr2; ut32 addr4, addr4_; ut64 addr8; switch (input[1]) { case '?': r_cons_printf ("Usage: wv[size] [value] # write value of given size\n" " wv1 234 # write one byte with this value\n" " wv 0x834002 # write dword with this value\n" "Supported sizes are: 1, 2, 4, 8\n"); return 0; case '1': type = 1; break; case '2': type = 2; break; case '4': type = 4; break; case '8': type = 8; break; } off = r_num_math (core->num, input+2); r_io_set_fd (core->io, core->file->fd); r_io_seek (core->io, core->offset, R_IO_SEEK_SET); if (type == 0) type = (off&UT64_32U)? 8: 4; switch (type) { case 1: addr1 = (ut8)off; r_io_write (core->io, (const ut8 *)&addr1, 1); WSEEK (core, 1); break; case 2: addr2 = (ut16)off; r_io_write (core->io, (const ut8 *)&addr2, 2); WSEEK (core, 2); break; case 4: addr4_ = (ut32)off; //drop_endian((ut8*)&addr4_, (ut8*)&addr4, 4); /* addr4_ = addr4 */ //endian_memcpy((ut8*)&addr4, (ut8*)&addr4_, 4); /* addr4 = addr4_ */ memcpy ((ut8*)&addr4, (ut8*)&addr4_, 4); // XXX needs endian here too r_io_write (core->io, (const ut8 *)&addr4, 4); WSEEK (core, 4); break; case 8: /* 8 byte addr */ memcpy ((ut8*)&addr8, (ut8*)&off, 8); // XXX needs endian here // endian_memcpy((ut8*)&addr8, (ut8*)&off, 8); r_io_write (core->io, (const ut8 *)&addr8, 8); WSEEK (core, 8); break; } r_core_block_read (core, 0); } break; case 'o': switch (input[1]) { case 'a': case 's': case 'A': case 'x': case 'r': case 'l': case 'm': case 'd': case 'o': case 'w': if (input[2]!=' ') { r_cons_printf ("Usage: 'wo%c 00 11 22'\n", input[1]); return 0; } case '2': case '4': r_core_write_op (core, input+3, input[1]); r_core_block_read (core, 0); break; case 'n': r_core_write_op (core, "ff", 'x'); r_core_block_read (core, 0); break; case '\0': case '?': default: r_cons_printf ( "Usage: wo[asmdxoArl24] [hexpairs] @ addr[:bsize]\n" "Example:\n" " wox 0x90 ; xor cur block with 0x90\n" " wox 90 ; xor cur block with 0x90\n" " wox 0x0203 ; xor cur block with 0203\n" " woa 02 03 ; add [0203][0203][...] to curblk\n" "Supported operations:\n" " wow == write looped value (alias for 'wb')\n" " woa += addition\n" " wos -= substraction\n" " wom *= multiply\n" " wod /= divide\n" " wox ^= xor\n" " woo |= or\n" " woA &= and\n" " wor >>= shift right\n" " wol <<= shift left\n" " wo2 2= 2 byte endian swap\n" " wo4 4= 4 byte endian swap\n" ); break; } break; default: case '?': if (core->oobi) { eprintf ("Writing oobi buffer!\n"); r_io_set_fd (core->io, core->file->fd); r_io_write (core->io, core->oobi, core->oobi_len); WSEEK (core, core->oobi_len); r_core_block_read (core, 0); } else r_cons_printf ( "Usage: w[x] [str] [<file] [<<EOF] [@addr]\n" " w foobar write string 'foobar'\n" " wr 10 write 10 random bytes\n" " ww foobar write wide string 'f\\x00o\\x00o\\x00b\\x00a\\x00r\\x00'\n" " wa push ebp write opcode, separated by ';' (use '\"' around the command)\n" " waf file assemble file and write bytes\n" " wA r 0 alter/modify opcode at current seek (see wA?)\n" " wb 010203 fill current block with cyclic hexpairs\n" " wc[ir*?] write cache commit/reset/list\n" " wx 9090 write two intel nops\n" " wv eip+34 write 32-64 bit value\n" " wo? hex write in block with operation. 'wo?' fmi\n" " wm f0ff set binary mask hexpair to be used as cyclic write mask\n" " wf file write contents of file at current offset\n" " wF file write contents of hexpairs file here\n" " wt file [sz] write to file (from current seek, blocksize or sz bytes)\n" " wp file apply radare patch file. See wp? fmi\n"); //TODO: add support for offset+seek // " wf file o s ; write contents of file from optional offset 'o' and size 's'.\n" break; }
static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) { char *pstr, *str; //eprintf ("%d %d\n", d->space, a->meta_spaces.space_idx); if (a->meta_spaces.space_idx != -1) { if (a->meta_spaces.space_idx != d->space) { return; } } str = r_str_escape (d->str); if (str || d->type == 'd') { if (d->type=='s' && !*str) { free (str); return; } if (!str) { pstr = ""; } else if (d->type != 'C') { r_name_filter (str, 0); pstr = str; } else pstr = d->str; // r_str_sanitize (str); switch (rad) { case 'j': a->cb_printf ("{\"offset\":%"PFMT64d", \"type\":\"%s\", \"name\":\"%s\"}", d->from, r_meta_type_to_string (d->type), str); break; case 0: case 1: case '*': default: switch (d->type) { case 'C': { const char *type = r_meta_type_to_string (d->type); char *s = sdb_encode ((const ut8*)pstr, -1); if (!s) s = strdup (pstr); if (rad) { if (!strcmp (type, "CCu")) { a->cb_printf ("%s base64:%s @ 0x%08"PFMT64x"\n", type, s, d->from); } else { a->cb_printf ("%s %s @ 0x%08"PFMT64x"\n", type, pstr, d->from); } } else { if (!strcmp (type, "CCu")) { char *mys = r_str_escape (pstr); a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n", d->from, type, mys); free (mys); } else { a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n", d->from, type, pstr); } } free (s); } break; case 'h': /* hidden */ case 's': /* string */ if (rad) { a->cb_printf ("%s %d @ 0x%08"PFMT64x" # %s\n", r_meta_type_to_string (d->type), (int)d->size, d->from, pstr); } else { // TODO: use b64 here a->cb_printf ("0x%08"PFMT64x" string[%d] \"%s\"\n", d->from, (int)d->size, pstr); } break; case 'd': /* data */ if (rad) { a->cb_printf ("%s %d @ 0x%08"PFMT64x"\n", r_meta_type_to_string (d->type), (int)d->size, d->from); } else { a->cb_printf ("0x%08"PFMT64x" data %s %d\n", d->from, r_meta_type_to_string (d->type), (int)d->size); } break; case 'm': /* magic */ case 'f': /* formatted */ if (rad) { a->cb_printf ("%s %d %s @ 0x%08"PFMT64x"\n", r_meta_type_to_string (d->type), (int)d->size, pstr, d->from); } else { const char *dtype = d->type=='m'?"magic":"format"; a->cb_printf ("0x%08"PFMT64x" %s %d %s\n", d->from, dtype, (int)d->size, pstr); } break; default: if (rad) { a->cb_printf ("%s %d 0x%08"PFMT64x" # %s\n", r_meta_type_to_string (d->type), (int)d->size, d->from, pstr); } else { // TODO: use b64 here a->cb_printf ("0x%08"PFMT64x" array[%d] %s %s\n", d->from, (int)d->size, r_meta_type_to_string (d->type), pstr); } break; } break; } if (str) free (str); } }
R_API int r_sys_cmd_str_full(const char *cmd, const char *input, char **output, int *len, char **sterr) { char buffer[1024], *outputptr = NULL; char *inputptr = (char *)input; int pid, bytes = 0, status; int sh_in[2], sh_out[2], sh_err[2]; if (len) { *len = 0; } if (pipe (sh_in)) { return false; } if (output) { if (pipe (sh_out)) { close (sh_in[0]); close (sh_in[1]); close (sh_out[0]); close (sh_out[1]); return false; } } if (pipe (sh_err)) { close (sh_in[0]); close (sh_in[1]); return false; } switch ((pid = r_sys_fork ())) { case -1: return false; case 0: dup2 (sh_in[0], 0); close (sh_in[0]); close (sh_in[1]); if (output) { dup2 (sh_out[1], 1); close (sh_out[0]); close (sh_out[1]); } if (sterr) { dup2 (sh_err[1], 2); } else { close (2); } close (sh_err[0]); close (sh_err[1]); exit (r_sandbox_system (cmd, 0)); default: outputptr = strdup (""); if (!outputptr) { return false; } if (sterr) { *sterr = strdup (""); if (!*sterr) { free (outputptr); return false; } } if (output) { close (sh_out[1]); } close (sh_err[1]); close (sh_in[0]); if (!inputptr || !*inputptr) { close (sh_in[1]); } // we should handle broken pipes somehow better signal (SIGPIPE, SIG_IGN); for (;;) { fd_set rfds, wfds; int nfd; FD_ZERO (&rfds); FD_ZERO (&wfds); if (output) { FD_SET (sh_out[0], &rfds); } if (sterr) { FD_SET (sh_err[0], &rfds); } if (inputptr && *inputptr) { FD_SET (sh_in[1], &wfds); } memset (buffer, 0, sizeof (buffer)); nfd = select (sh_err[0] + 1, &rfds, &wfds, NULL, NULL); if (nfd < 0) { break; } if (output && FD_ISSET (sh_out[0], &rfds)) { if (!(bytes = read (sh_out[0], buffer, sizeof (buffer)-1))) { break; } buffer[sizeof(buffer) - 1] = '\0'; if (len) { *len += bytes; } outputptr = r_str_append (outputptr, buffer); } else if (FD_ISSET (sh_err[0], &rfds) && sterr) { if (!read (sh_err[0], buffer, sizeof (buffer)-1)) { break; } buffer[sizeof(buffer) - 1] = '\0'; *sterr = r_str_append (*sterr, buffer); } else if (FD_ISSET (sh_in[1], &wfds) && inputptr && *inputptr) { int inputptr_len = strlen (inputptr); bytes = write (sh_in[1], inputptr, inputptr_len); if (bytes != inputptr_len) { break; } inputptr += bytes; if (!*inputptr) { close (sh_in[1]); /* If neither stdout nor stderr should be captured, * abort now - nothing more to do for select(). */ if (!output && !sterr) { break; } } } } if (output) { close (sh_out[0]); } close (sh_err[0]); close (sh_in[1]); waitpid (pid, &status, 0); bool ret = true; if (status) { char *escmd = r_str_escape (cmd); eprintf ("error code %d\n", WEXITSTATUS(status)); //eprintf ("%s: failed command '%s'\n", __func__, escmd); free (escmd); ret = false; } if (output) { *output = outputptr; } else { free (outputptr); } return ret; } return false; }