blob: a2acb1926d004944ecf79eb7e131ae67978a51c9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2014 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* TCF Processes - process control service.
* Processes service provides access to the target OS's process information,
* allows to start and terminate a process, and allows to attach and
* detach a process for debugging. Debug services, like Memory and Run Control,
* require a process to be attached before they can access it.
*/
/* TODO: It should be possible to filter processes on a criteria (user, name, etc) */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif
#include <tcf/config.h>
#if SERVICE_Processes || SERVICE_Terminals
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <assert.h>
#include <tcf/framework/mdep-fs.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/protocol.h>
#include <tcf/framework/trace.h>
#include <tcf/framework/context.h>
#include <tcf/framework/json.h>
#include <tcf/framework/asyncreq.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/waitpid.h>
#include <tcf/framework/signames.h>
#include <tcf/framework/mdep-fs.h>
#include <tcf/services/streamsservice.h>
#include <tcf/services/runctrl.h>
#include <tcf/services/processes.h>
#if SERVICE_Processes
static const char * PROCESSES[2] = { "Processes", "ProcessesV1" };
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
# include <tlhelp32.h>
# ifdef _MSC_VER
# pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union (in winternl.h) */
# include <winternl.h>
# else
# include <ntdef.h>
# endif
# ifndef STATUS_INFO_LENGTH_MISMATCH
# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
# endif
# ifndef SystemHandleInformation
# define SystemHandleInformation 16
# endif
#elif defined(_WRS_KERNEL)
# include <symLib.h>
# include <sysSymTbl.h>
# include <ioLib.h>
# include <ptyDrv.h>
# include <taskHookLib.h>
#else
# include <termios.h>
# ifndef TIOCGWINSZ
# include <sys/ioctl.h>
# endif
# include <sys/stat.h>
# include <unistd.h>
#endif
#define PBUF_SIZE 0x400
#define PIPE_SIZE 0x400
#define SBUF_SIZE 0x1000
typedef struct AttachDoneArgs {
Channel * c;
char token[256];
SigSet sig_dont_stop;
SigSet sig_dont_pass;
int set_dont_stop;
int set_dont_pass;
} AttachDoneArgs;
struct ChildProcess {
LINK link;
int pid;
int tty;
int got_output;
TCFBroadcastGroup * bcg;
struct ProcessInput * inp_struct;
struct ProcessOutput * out_struct;
struct ProcessOutput * err_struct;
char name[256];
char service[256];
long exit_code;
EventCallBack * exit_cb;
void * exit_args;
unsigned ws_col;
unsigned ws_row;
};
typedef struct ProcessOutput {
int fd;
char id[256];
ChildProcess * prs;
AsyncReqInfo req;
int req_posted;
char buf[PBUF_SIZE];
size_t buf_pos;
int eos;
VirtualStream * vstream;
} ProcessOutput;
typedef struct ProcessInput {
int fd;
char id[256];
ChildProcess * prs;
AsyncReqInfo req;
int req_posted;
char buf[PBUF_SIZE];
size_t buf_pos;
size_t buf_len;
int eos;
VirtualStream * vstream;
} ProcessInput;
#define link2prs(A) ((ChildProcess *)((char *)(A) - offsetof(ChildProcess, link)))
static int init_done = 0;
static LINK prs_list = TCF_LIST_INIT(prs_list);
#if defined(_WRS_KERNEL)
static SEM_ID prs_list_lock = NULL;
#endif
static ChildProcess * find_process(int pid) {
LINK * qhp = &prs_list;
LINK * qp = qhp->next;
while (qp != qhp) {
ChildProcess * prs = link2prs(qp);
if (prs->pid == pid) return prs;
qp = qp->next;
}
return NULL;
}
#if SERVICE_Processes
static int is_attached(pid_t pid) {
#if ENABLE_DebugContext
return context_find_from_pid(pid, 0) != NULL;
#else
return 0;
#endif
}
static void write_context(OutputStream * out, int pid) {
ChildProcess * prs = find_process(pid);
write_stream(out, '{');
/* the process Name */
#if defined(__linux__)
/* Use the /proc to get the name */
{
char buff[256];
char fname[256];
FILE * file;
const char * name = NULL;
/* clear out buff */
buff[0] = 0;
/* try cmdline */
snprintf(fname, sizeof(fname), "/proc/%d/cmdline", pid);
file = fopen(fname, "r");
if (file) {
if (fgets(buff, sizeof(buff), file) != NULL) {
if (buff[0]) name = buff;
}
fclose(file);
}
if (!name) {
/* try status */
snprintf(fname, sizeof(fname), "/proc/%d/status", pid);
file = fopen(fname, "r");
if (file) {
char * p = fgets(buff, sizeof(buff), file);
if (p != NULL) {
/* Find the attribute name */
for (; *p; ++p) {
if (*p == ':') {
/* close off the attr name string */
*p++ = 0;
/* is it our name? */
if (!strcmp(buff, "Name")) {
char * n;
/* change tab to '[' */
*p = '[';
/* change trailing new line to ']' */
for (n = p; *n; ++n)
if (*n == '\n')
*n = ']';
name = p;
break;
}
}
}
}
fclose(file);
}
}
if (!name) name = pid2id(pid, 0);
/* Send it out */
json_write_string(out, "Name");
write_stream(out, ':');
json_write_string(out, name);
write_stream(out, ',');
}
#else
json_write_string(out, "Name");
write_stream(out, ':');
json_write_string(out, prs ? prs->name : pid2id(pid, 0));
write_stream(out, ',');
#endif
json_write_string(out, "CanTerminate");
write_stream(out, ':');
json_write_boolean(out, 1);
write_stream(out, ',');
if (is_attached(pid)) {
json_write_string(out, "Attached");
write_stream(out, ':');
json_write_boolean(out, 1);
write_stream(out, ',');
}
if (prs != NULL) {
if (prs->inp_struct) {
json_write_string(out, "StdInID");
write_stream(out, ':');
json_write_string(out, prs->inp_struct->id);
write_stream(out, ',');
}
if (prs->out_struct) {
json_write_string(out, "StdOutID");
write_stream(out, ':');
json_write_string(out, prs->out_struct->id);
write_stream(out, ',');
}
if (prs->err_struct) {
json_write_string(out, "StdErrID");
write_stream(out, ':');
json_write_string(out, prs->err_struct->id);
write_stream(out, ',');
}
}
json_write_string(out, "ID");
write_stream(out, ':');
json_write_string(out, pid2id(pid, 0));
write_stream(out, '}');
}
static void send_event_process_exited(OutputStream * out, ChildProcess * prs) {
int v;
for (v = 0; v < 2; v++) {
write_stringz(out, "E");
write_stringz(out, PROCESSES[v]);
write_stringz(out, "exited");
json_write_string(out, pid2id(prs->pid, 0));
write_stream(out, 0);
json_write_long(out, prs->exit_code);
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
}
static void command_get_context(char * token, Channel * c) {
int err = 0;
char id[256];
pid_t pid, parent;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
pid = id2pid(id, &parent);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
if (pid != 0 && parent == 0) {
#if defined(_WIN32) || defined(__CYGWIN__)
#elif defined(_WRS_KERNEL)
if (TASK_ID_VERIFY(pid) == ERROR) err = ERR_INV_CONTEXT;
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
#else
struct stat st;
char dir[FILE_PATH_SIZE];
snprintf(dir, sizeof(dir), "/proc/%d", pid);
if (lstat(dir, &st) < 0) err = errno;
else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
#endif
}
write_errno(&c->out, err);
if (err == 0 && pid != 0 && parent == 0) {
write_context(&c->out, pid);
write_stream(&c->out, 0);
}
else {
write_stringz(&c->out, "null");
}
write_stream(&c->out, MARKER_EOM);
}
static void command_get_children(char * token, Channel * c) {
char id[256];
int attached_only;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
attached_only = json_read_boolean(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
if (id[0] != 0) {
write_errno(&c->out, 0);
write_stringz(&c->out, "null");
}
else {
#if defined(_WIN32) || defined(__CYGWIN__)
DWORD err = 0;
HANDLE snapshot;
PROCESSENTRY32 pe32;
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) err = set_win32_errno(GetLastError());
memset(&pe32, 0, sizeof(pe32));
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!err && !Process32First(snapshot, &pe32)) {
err = set_win32_errno(GetLastError());
CloseHandle(snapshot);
}
write_errno(&c->out, err);
if (err) {
write_stringz(&c->out, "null");
}
else {
int cnt = 0;
write_stream(&c->out, '[');
do {
if (!attached_only || is_attached(pe32.th32ProcessID)) {
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, pid2id(pe32.th32ProcessID, 0));
cnt++;
}
}
while (Process32Next(snapshot, &pe32));
write_stream(&c->out, ']');
write_stream(&c->out, 0);
}
if (snapshot != INVALID_HANDLE_VALUE) CloseHandle(snapshot);
#elif defined(_WRS_KERNEL)
int i = 0;
int cnt = 0;
int ids_cnt = 0;
int ids_max = 500;
int * ids = (int *)loc_alloc(ids_max * sizeof(int));
for (;;) {
ids_cnt = taskIdListGet(ids, ids_max);
if (ids_cnt < ids_max) break;
loc_free(ids);
ids_max *= 2;
ids = (int *)loc_alloc(ids_max * sizeof(int));
}
write_errno(&c->out, 0);
write_stream(&c->out, '[');
for (i = 0; i < ids_cnt; i++) {
if (!attached_only || is_attached(ids[i])) {
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, pid2id(ids[i], 0));
cnt++;
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
#else
DIR * proc = opendir("/proc");
if (proc == NULL) {
write_errno(&c->out, errno);
write_stringz(&c->out, "null");
}
else {
int cnt = 0;
write_errno(&c->out, 0);
write_stream(&c->out, '[');
for (;;) {
struct dirent * ent = readdir(proc);
if (ent == NULL) break;
if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') {
pid_t pid = atol(ent->d_name);
if (!attached_only || is_attached(pid)) {
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, pid2id(pid, 0));
cnt++;
}
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
closedir(proc);
}
#endif
}
write_stream(&c->out, MARKER_EOM);
}
#if ENABLE_DebugContext
static void attach_done(int error, Context * ctx, void * arg) {
AttachDoneArgs * data = (AttachDoneArgs *)arg;
Channel * c = data->c;
if (!is_channel_closed(c)) {
write_stringz(&c->out, "R");
write_stringz(&c->out, data->token);
write_errno(&c->out, error);
write_stream(&c->out, MARKER_EOM);
}
channel_unlock_with_msg(c, PROCESSES[0]);
loc_free(data);
}
static void command_attach(char * token, Channel * c) {
int err = 0;
char id[256];
pid_t pid, parent;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
pid = id2pid(id, &parent);
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else if (is_attached(pid)) {
err = ERR_ALREADY_ATTACHED;
}
else {
AttachDoneArgs * data = (AttachDoneArgs *)loc_alloc_zero(sizeof *data);
data->c = c;
strcpy(data->token, token);
if (context_attach(pid, attach_done, data, 0) == 0) {
channel_lock_with_msg(c, PROCESSES[0]);
return;
}
err = errno;
loc_free(data);
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void command_detach(char * token, Channel * c) {
int err = 0;
char id[256];
Context * ctx = NULL;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
ctx = id2ctx(id);
if (ctx == NULL) err = ERR_INV_CONTEXT;
if (!err && ctx->exited) err = ERR_ALREADY_EXITED;
if (!err && detach_debug_context(ctx) < 0) err = errno;
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void write_sigset(OutputStream * out, SigSet * set) {
unsigned bit = 0;
uint64_t bits = 0;
while (sigset_get_next(set, &bit)) {
if (bit >= sizeof(bits) * 8) {
/* Does not fit into int bitset */
unsigned cnt = 0;
bit = 0;
write_stream(out, '[');
while (sigset_get_next(set, &bit)) {
if (cnt > 0) write_stream(out, ',');
json_write_ulong(out, bit);
cnt++;
}
write_stream(out, ']');
return;
}
bits |= (uint64_t)1 << bit;
}
json_write_uint64(out, bits);
}
static void command_get_signal_mask(char * token, Channel * c) {
int err = 0;
char id[256];
Context * ctx = NULL;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
ctx = id2ctx(id);
if (ctx == NULL) err = ERR_INV_CONTEXT;
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (ctx == NULL) {
write_stringz(&c->out, "null");
write_stringz(&c->out, "null");
write_stringz(&c->out, "null");
}
else {
write_sigset(&c->out, &ctx->sig_dont_stop);
write_stream(&c->out, 0);
write_sigset(&c->out, &ctx->sig_dont_pass);
write_stream(&c->out, 0);
write_sigset(&c->out, &ctx->pending_signals);
write_stream(&c->out, 0);
}
write_stream(&c->out, MARKER_EOM);
}
static void read_sigset_bit(InputStream * inp, void * args) {
SigSet * set = (SigSet *)args;
unsigned bit = (unsigned)json_read_ulong(inp);
sigset_set(set, bit, 1);
}
static void read_sigset(InputStream * inp, SigSet * set, int * not_null) {
memset(set, 0, sizeof(SigSet));
if (json_peek(inp) == 'n') {
read_stream(inp);
json_test_char(inp, 'u');
json_test_char(inp, 'l');
json_test_char(inp, 'l');
*not_null = 0;
}
else if (json_peek(inp) == '[') {
json_read_array(inp, read_sigset_bit, set);
*not_null = 1;
}
else {
unsigned bit;
uint64_t bits = json_read_uint64(inp);
for (bit = 0; bit < sizeof(bits) * 8; bit++) {
sigset_set(set, bit, (bits & ((uint64_t)1 << bit)) != 0);
}
*not_null = 1;
}
}
static void get_deafult_sig_dont_stop(SigSet * set) {
int i;
int n = signal_cnt();
for (i = 0; i < n; i++) {
const char * nm = signal_name(i);
if (nm == NULL) continue;
if (strcmp(nm, "SIGCHLD") == 0 ||
strcmp(nm, "SIGPIPE") == 0 ||
strcmp(nm, "SIGWINCH") == 0) {
sigset_set(set, i, 1);
}
}
}
static void get_deafult_sig_dont_pass(SigSet * set) {
/* Empty */
}
static void command_set_signal_mask(char * token, Channel * c) {
int err = 0;
char id[256];
Context * ctx = NULL;
SigSet dont_stop;
SigSet dont_pass;
int set_dont_stop = 0;
int set_dont_pass = 0;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
read_sigset(&c->inp, &dont_stop, &set_dont_stop);
json_test_char(&c->inp, MARKER_EOA);
read_sigset(&c->inp, &dont_pass, &set_dont_pass);
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
ctx = id2ctx(id);
if (ctx == NULL) {
err = ERR_INV_CONTEXT;
sigset_clear(&dont_stop);
sigset_clear(&dont_pass);
}
else {
sigset_clear(&ctx->sig_dont_stop);
sigset_clear(&ctx->sig_dont_pass);
if (set_dont_stop) ctx->sig_dont_stop = dont_stop;
else get_deafult_sig_dont_stop(&ctx->sig_dont_stop);
if (set_dont_pass) ctx->sig_dont_pass = dont_pass;
else get_deafult_sig_dont_pass(&ctx->sig_dont_pass);
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void start_done(int error, Context * ctx, void * arg) {
AttachDoneArgs * data = (AttachDoneArgs *)arg;
Channel * c = data->c;
if (ctx == NULL) {
sigset_clear(&data->sig_dont_stop);
sigset_clear(&data->sig_dont_pass);
}
else {
LINK * l = ctx->children.next;
while (l != &ctx->children) {
Context * x = cldl2ctxp(l);
sigset_clear(&x->sig_dont_stop);
sigset_clear(&x->sig_dont_pass);
if (data->set_dont_stop) sigset_copy(&x->sig_dont_stop, &data->sig_dont_stop);
else get_deafult_sig_dont_stop(&x->sig_dont_stop);
if (data->set_dont_pass) sigset_copy(&x->sig_dont_pass, &data->sig_dont_pass);
else get_deafult_sig_dont_pass(&x->sig_dont_pass);
l = l->next;
}
sigset_clear(&ctx->sig_dont_stop);
sigset_clear(&ctx->sig_dont_pass);
if (data->set_dont_stop) ctx->sig_dont_stop = data->sig_dont_stop;
else get_deafult_sig_dont_stop(&ctx->sig_dont_stop);
if (data->set_dont_pass) ctx->sig_dont_pass = data->sig_dont_pass;
else get_deafult_sig_dont_pass(&ctx->sig_dont_pass);
}
if (!is_channel_closed(c)) {
write_stringz(&c->out, "R");
write_stringz(&c->out, data->token);
write_errno(&c->out, error);
if (ctx == NULL) write_string(&c->out, "null");
else write_context(&c->out, id2pid(ctx->id, NULL));
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
channel_unlock_with_msg(c, PROCESSES[0]);
loc_free(data);
}
#endif /* ENABLE_DebugContext */
static void command_terminate(char * token, Channel * c) {
int err = 0;
char id[256];
pid_t pid, parent;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
pid = id2pid(id, &parent);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else {
#if defined(_WIN32) || defined(__CYGWIN__)
HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (h == NULL) {
err = set_win32_errno(GetLastError());
}
else {
if (!TerminateProcess(h, 1)) err = set_win32_errno(GetLastError());
if (!CloseHandle(h) && !err) err = set_win32_errno(GetLastError());
}
#else
if (kill(pid, SIGTERM) < 0) err = errno;
#endif
}
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void command_signal(char * token, Channel * c) {
int err = 0;
char id[256];
int signal = 0;
pid_t pid, parent;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
signal = (int)json_read_long(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
pid = id2pid(id, &parent);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
#if defined(_WIN32) || defined(__CYGWIN__)
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else if (signal_code(signal) == 0x40010005) {
/* Control-C */
HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (h == NULL) {
err = set_win32_errno(GetLastError());
}
else {
if (!TerminateProcess(h, 1)) err = set_win32_errno(GetLastError());
if (!CloseHandle(h) && !err) err = set_win32_errno(GetLastError());
}
}
else {
err = ENOSYS;
}
#elif defined(_WRS_KERNEL)
if (kill(pid, signal) < 0) err = errno;
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
if (kill(pid, signal) < 0) err = errno;
#else
if (parent == 0) {
if (kill(pid, signal) < 0) err = errno;
}
else {
if (tkill(pid, signal) < 0) err = errno;
}
#endif
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void command_get_signal_list(char * token, Channel * c) {
int err = 0;
char id[256];
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
/* pid is ignored, same signal list for all */
id2pid(id, NULL);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (err) {
write_stringz(&c->out, "null");
}
else {
int i = 0;
int n = 0;
int cnt = signal_cnt();
write_stream(&c->out, '[');
for (i = 0; i < cnt; i++) {
const char * name = signal_name(i);
const char * desc = signal_description(i);
if (name != NULL || desc != NULL) {
if (n > 0) write_stream(&c->out, ',');
write_stream(&c->out, '{');
json_write_string(&c->out, "Index");
write_stream(&c->out, ':');
json_write_long(&c->out, i);
if (name != NULL) {
write_stream(&c->out, ',');
json_write_string(&c->out, "Name");
write_stream(&c->out, ':');
json_write_string(&c->out, name);
}
if (desc != NULL) {
write_stream(&c->out, ',');
json_write_string(&c->out, "Description");
write_stream(&c->out, ':');
json_write_string(&c->out, desc);
}
write_stream(&c->out, ',');
json_write_string(&c->out, "Code");
write_stream(&c->out, ':');
json_write_ulong(&c->out, signal_code(i));
write_stream(&c->out, '}');
n++;
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
}
write_stream(&c->out, MARKER_EOM);
}
static void command_get_environment(char * token, Channel * c) {
char ** p = environ;
json_test_char(&c->inp, MARKER_EOM);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, 0);
write_stream(&c->out, '[');
if (p != NULL) {
while (*p != NULL) {
if (p != environ) write_stream(&c->out, ',');
json_write_string(&c->out, *p++);
}
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
#endif
static void process_exited(ChildProcess * prs) {
#if SERVICE_Processes
send_event_process_exited(&prs->bcg->out, prs);
#endif
#if defined(_WRS_KERNEL)
semTake(prs_list_lock, WAIT_FOREVER);
#endif
list_remove(&prs->link);
if (prs->inp_struct) {
ProcessInput * inp = prs->inp_struct;
if (!inp->req_posted) {
virtual_stream_delete(inp->vstream);
close(inp->fd);
loc_free(inp);
}
else {
inp->prs = NULL;
}
}
if (prs->out_struct) prs->out_struct->prs = NULL;
if (prs->err_struct) prs->err_struct->prs = NULL;
if (prs->exit_cb) prs->exit_cb(prs->exit_args);
loc_free(prs);
#if defined(_WRS_KERNEL)
semGive(prs_list_lock);
#endif
}
static void process_input_streams_callback(VirtualStream * stream, int event_code, void * args) {
ProcessInput * inp = (ProcessInput *)args;
assert(inp->vstream == stream);
if (!inp->req_posted) {
if (inp->buf_pos >= inp->buf_len && !inp->eos) {
inp->buf_pos = inp->buf_len = 0;
virtual_stream_get_data(stream, inp->buf, sizeof(inp->buf), &inp->buf_len, &inp->eos);
}
if (inp->buf_pos < inp->buf_len) {
inp->req.u.fio.bufp = inp->buf + inp->buf_pos;
inp->req.u.fio.bufsz = inp->buf_len - inp->buf_pos;
inp->req_posted = 1;
async_req_post(&inp->req);
}
}
}
static void write_process_input_done(void * x) {
AsyncReqInfo * req = (AsyncReqInfo *)x;
ProcessInput * inp = (ProcessInput *)req->client_data;
inp->req_posted = 0;
if (inp->prs == NULL) {
/* Process has exited */
virtual_stream_delete(inp->vstream);
close(inp->fd);
loc_free(inp);
}
else {
int wr = inp->req.u.fio.rval;
if (wr < 0) {
trace(LOG_ALWAYS, "Can't write process input stream: %s", errno_to_str(inp->req.error));
inp->buf_pos = inp->buf_len = 0;
}
else {
inp->buf_pos += wr;
}
process_input_streams_callback(inp->vstream, 0, inp);
}
}
static ProcessInput * write_process_input(ChildProcess * prs, int fd) {
ProcessInput * inp = (ProcessInput *)loc_alloc_zero(sizeof(ProcessInput));
inp->fd = fd;
inp->prs = prs;
inp->req.client_data = inp;
inp->req.done = write_process_input_done;
inp->req.type = AsyncReqWrite;
inp->req.u.fio.fd = fd;
virtual_stream_create(prs->service, pid2id(prs->pid, 0), SBUF_SIZE, VS_ENABLE_REMOTE_WRITE,
process_input_streams_callback, inp, &inp->vstream);
virtual_stream_get_id(inp->vstream, inp->id, sizeof(inp->id));
return inp;
}
static void post_out_read_req(void * args) {
ProcessOutput * out = (ProcessOutput *)args;
assert(out->req_posted);
async_req_post(&out->req);
}
static void process_output_streams_callback(VirtualStream * stream, int event_code, void * args) {
ProcessOutput * out = (ProcessOutput *)args;
assert(out->vstream == stream);
if (!out->req_posted) {
int buf_len = out->req.u.fio.rval;
int err = 0;
int eos = 0;
if (buf_len < 0) {
buf_len = 0;
err = out->req.error;
}
if (buf_len == 0) eos = 1;
if (err && out->prs == NULL) err = 0;
assert((size_t)buf_len <= sizeof(out->buf));
assert(out->buf_pos <= (size_t)buf_len);
assert(out->req.u.fio.bufp == out->buf);
#ifdef __linux__
if (err == EIO) err = 0;
#endif
if (err) trace(LOG_ALWAYS, "Can't read process output stream: %d %s", err, errno_to_str(err));
if (out->buf_pos < (size_t)buf_len || out->eos != eos) {
size_t done = 0;
virtual_stream_add_data(stream, out->buf + out->buf_pos, buf_len - out->buf_pos, &done, eos);
out->buf_pos += done;
assert(out->buf_pos <= (size_t)buf_len);
if (out->buf_pos == (size_t)buf_len && eos) out->eos = 1;
}
if (out->buf_pos >= (size_t)buf_len) {
if (!eos) {
out->req_posted = 1;
post_event_with_delay(post_out_read_req, out, 10000);
}
else if (virtual_stream_is_empty(stream)) {
if (out->prs != NULL) {
if (out == out->prs->out_struct) out->prs->out_struct = NULL;
if (out == out->prs->err_struct) out->prs->err_struct = NULL;
}
virtual_stream_delete(stream);
close(out->fd);
loc_free(out);
}
}
}
}
static void read_process_output_done(void * x) {
AsyncReqInfo * req = (AsyncReqInfo *)x;
ProcessOutput * out = (ProcessOutput *)req->client_data;
out->buf_pos = 0;
out->req_posted = 0;
if (out->prs && out->req.u.fio.rval > 0) out->prs->got_output = 1;
process_output_streams_callback(out->vstream, 0, out);
}
static ProcessOutput * read_process_output(ChildProcess * prs, int fd) {
ProcessOutput * out = (ProcessOutput *)loc_alloc_zero(sizeof(ProcessOutput));
out->fd = fd;
out->prs = prs;
out->req.client_data = out;
out->req.done = read_process_output_done;
out->req.type = AsyncReqRead;
out->req.u.fio.bufp = out->buf;
out->req.u.fio.bufsz = sizeof(out->buf);
out->req.u.fio.fd = fd;
virtual_stream_create(prs->service, pid2id(prs->pid, 0), SBUF_SIZE, VS_ENABLE_REMOTE_READ,
process_output_streams_callback, out, &out->vstream);
virtual_stream_get_id(out->vstream, out->id, sizeof(out->id));
out->req_posted = 1;
async_req_post(&out->req);
return out;
}
#if !ENABLE_DebugContext
# define context_attach(pid, done, client_data, mode) (errno = ERR_UNSUPPORTED, -1)
# define context_attach_self() (errno = ERR_UNSUPPORTED, -1)
#endif
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
#if defined(_MSC_VER)
static int setenv(const char * name, const char * val, int overwrite) {
assert(overwrite);
_putenv_s(name, val);
return 0;
}
#elif defined(__MINGW32__)
static int setenv(const char * name, const char * val, int overwrite) {
int len = strlen(name) + strlen(val) + 2;
char * str = (char *)loc_alloc(len);
snprintf(str, len, "%s=%s", name, val);
putenv(str);
return 0;
}
#endif
static int start_process_imp(Channel * c, char ** envp, const char * dir, const char * exe, char ** args,
ProcessStartParams * params, int * selfattach, ChildProcess ** prs) {
typedef struct _HANDLE_INFORMATION {
USHORT ProcessId;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} HANDLE_INFORMATION;
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG Count;
HANDLE_INFORMATION Handles[1];
} SYSTEM_HANDLE_INFORMATION;
typedef NTSTATUS (FAR WINAPI * QuerySystemInformationTypedef)(int, PVOID, ULONG, PULONG);
QuerySystemInformationTypedef QuerySystemInformationProc = (QuerySystemInformationTypedef)GetProcAddress(
GetModuleHandle("NTDLL.DLL"), "NtQuerySystemInformation");
DWORD size;
DWORD pid = 0;
NTSTATUS status;
SYSTEM_HANDLE_INFORMATION * hi = NULL;
int fpipes[3][2];
HANDLE hpipes[3][2];
struct stat st;
int err = 0;
int i;
size = sizeof(SYSTEM_HANDLE_INFORMATION) + sizeof(HANDLE_INFORMATION) * 256;
hi = (SYSTEM_HANDLE_INFORMATION *)tmp_alloc_zero(size);
for (;;) {
status = QuerySystemInformationProc(SystemHandleInformation, hi, size, &size);
if (status != STATUS_INFO_LENGTH_MISMATCH) break;
hi = (SYSTEM_HANDLE_INFORMATION *)tmp_realloc(hi, size);
}
if (status == 0) {
ULONG i;
DWORD id = GetCurrentProcessId();
for (i = 0; i < hi->Count; i++) {
if (hi->Handles[i].ProcessId != id) continue;
SetHandleInformation((HANDLE)(uintptr_t)hi->Handles[i].Handle, HANDLE_FLAG_INHERIT, FALSE);
}
}
else {
err = set_win32_errno(status);
}
memset(hpipes, 0, sizeof(hpipes));
for (i = 0; i < 3; i++) fpipes[i][0] = fpipes[i][1] = -1;
if (!err) {
#if defined(__CYGWIN__)
for (i = 0; i < 3; i++) {
if (pipe(fpipes[i]) < 0) {
err = errno;
break;
}
hpipes[i][0] = (HANDLE)get_osfhandle(fpipes[i][0]);
hpipes[i][1] = (HANDLE)get_osfhandle(fpipes[i][1]);
}
#else
for (i = 0; i < 3; i++) {
if (!CreatePipe(&hpipes[i][0], &hpipes[i][1], NULL, PIPE_SIZE)) {
err = set_win32_errno(GetLastError());
break;
}
fpipes[i][0] = _open_osfhandle((intptr_t)hpipes[i][0], O_TEXT);
fpipes[i][1] = _open_osfhandle((intptr_t)hpipes[i][1], O_TEXT);
}
#endif
}
if (!err && params->dir != NULL && chdir(params->dir) < 0) err = errno;
if (!err) {
STARTUPINFO si;
PROCESS_INFORMATION prs_info;
const char * fnm = exe;
char * cmd = NULL;
char * env = NULL;
const char * env_path = NULL;
SetHandleInformation(hpipes[0][0], HANDLE_FLAG_INHERIT, TRUE);
SetHandleInformation(hpipes[1][1], HANDLE_FLAG_INHERIT, TRUE);
SetHandleInformation(hpipes[2][1], HANDLE_FLAG_INHERIT, TRUE);
memset(&si, 0, sizeof(si));
memset(&prs_info, 0, sizeof(prs_info));
si.cb = sizeof(si);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = hpipes[0][0];
si.hStdOutput = hpipes[1][1];
si.hStdError = hpipes[2][1];
if (args != NULL) {
int i = 0;
int cmd_size = 0;
int cmd_pos = 0;
# define cmd_append(ch) { \
if (cmd_pos >= cmd_size) { \
cmd_size += 0x1000; \
cmd = (char *)tmp_realloc(cmd, cmd_size); \
} \
cmd[cmd_pos++] = (ch); \
}
while (args[i] != NULL) {
const char * p = args[i++];
if (cmd_pos > 0) cmd_append(' ');
cmd_append('"');
while (*p) {
if (*p == '"') cmd_append('\\');
cmd_append(*p);
p++;
}
cmd_append('"');
}
cmd_append(0);
# undef cmd_append
}
if (envp != NULL) {
if (*envp == NULL) {
env = (char *)tmp_alloc_zero(2);
}
else {
char ** p = envp;
size_t env_size = 1;
char * s = NULL;
env_path = getenv("PATH");
if (env_path == NULL) env_path = "";
env_path = tmp_strdup(env_path);
while (*p != NULL) env_size += strlen(*p++) + 1;
s = env = (char *)tmp_alloc(env_size);
for (p = envp; *p != NULL; p++) {
size_t l = strlen(*p) + 1;
if (strncmp(*p, "PATH=", 5) == 0) setenv("PATH", *p + 5, 1);
memcpy(s, *p, l);
s += l;
}
*s++ = 0;
assert((size_t)(s - env) == env_size);
}
}
/* If 'exe' is not a full file path, use PATH to search for program file */
if (stat(exe, &st) != 0) fnm = NULL;
if (CreateProcess(fnm, cmd, NULL, NULL, TRUE,
(params->attach ? CREATE_SUSPENDED : 0),
(LPVOID)env, dir, &si, &prs_info) == 0)
{
err = set_win32_errno(GetLastError());
}
else {
pid = prs_info.dwProcessId;
if (!CloseHandle(prs_info.hThread)) err = set_win32_errno(GetLastError());
if (!CloseHandle(prs_info.hProcess)) err = set_win32_errno(GetLastError());
}
if (env_path != NULL) setenv("PATH", env_path, 1);
}
if (fpipes[0][0] >= 0 && close(fpipes[0][0]) < 0 && !err) err = errno;
if (fpipes[1][1] >= 0 && close(fpipes[1][1]) < 0 && !err) err = errno;
if (fpipes[2][1] >= 0 && close(fpipes[2][1]) < 0 && !err) err = errno;
if (!err) {
*prs = (ChildProcess *)loc_alloc_zero(sizeof(ChildProcess));
(*prs)->pid = pid;
(*prs)->tty = -1;
(*prs)->bcg = c->bcg;
strlcpy((*prs)->service, params->service, sizeof((*prs)->service));
(*prs)->inp_struct = write_process_input(*prs, fpipes[0][1]);
(*prs)->out_struct = read_process_output(*prs, fpipes[1][0]);
(*prs)->err_struct = read_process_output(*prs, fpipes[2][0]);
list_add_first(&(*prs)->link, &prs_list);
}
else {
if (fpipes[0][1] >= 0) close(fpipes[0][1]);
if (fpipes[1][0] >= 0) close(fpipes[1][0]);
if (fpipes[2][0] >= 0) close(fpipes[2][0]);
}
if (!err) return 0;
trace(LOG_ALWAYS, "Can't start process '%s': %s", exe, errno_to_str(err));
errno = err;
return -1;
}
#elif defined(_WRS_KERNEL)
static void task_create_hook(WIND_TCB * tcb) {
ChildProcess * prs;
semTake(prs_list_lock, WAIT_FOREVER);
prs = find_process(taskIdSelf());
/* TODO: vxWork: standard IO inheritance */
semGive(prs_list_lock);
}
static void task_delete_hook(WIND_TCB * tcb) {
int i;
ChildProcess * prs;
semTake(prs_list_lock, WAIT_FOREVER);
prs = find_process((UINT32)tcb);
if (prs != NULL) {
close(ioTaskStdGet(prs->pid, 1));
close(ioTaskStdGet(prs->pid, 2));
for (i = 0; i < 2; i++) {
char pnm[32];
snprintf(pnm, sizeof(pnm), "/pty/tcf-%0*lx-%d", sizeof(prs->pid) * 2, prs->pid, i);
ptyDevRemove(pnm);
}
}
semGive(prs_list_lock);
}
static int start_process_imp(Channel * c, char ** envp, const char * dir, const char * exe, char ** args,
ProcessStartParams * params, int * selfattach, ChildProcess ** prs) {
int err = 0;
char * ptr;
SYM_TYPE type;
if (symFindByName(sysSymTbl, (char *)exe, &ptr, &type) != OK) {
err = errno;
if (err == S_symLib_SYMBOL_NOT_FOUND) err = ERR_SYM_NOT_FOUND;
assert(err != 0);
}
else {
int i;
int pipes[2][2];
/* TODO: arguments, environment */
int pid = taskCreate("tTcf", 100, 0, 0x4000, (FUNCPTR)ptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
for (i = 0; i < 2; i++) {
char pnm[32];
char pnm_m[32];
char pnm_s[32];
snprintf(pnm, sizeof(pnm), "/pty/tcf-%0*lx-%d", sizeof(pid) * 2, pid, i);
snprintf(pnm_m, sizeof(pnm_m), "%sM", pnm);
snprintf(pnm_s, sizeof(pnm_m), "%sS", pnm);
if (ptyDevCreate(pnm, PIPE_SIZE, PIPE_SIZE) == ERROR) {
err = errno;
break;
}
pipes[i][0] = open(pnm_m, O_RDWR, 0);
pipes[i][1] = open(pnm_s, O_RDWR, 0);
if (pipes[i][0] < 0 || pipes[i][1] < 0) {
err = errno;
break;
}
}
if (err) {
taskDelete(pid);
pid = 0;
}
else {
semTake(prs_list_lock, WAIT_FOREVER);
ioTaskStdSet(pid, 0, pipes[0][1]);
ioTaskStdSet(pid, 1, pipes[0][1]);
ioTaskStdSet(pid, 2, pipes[1][1]);
*prs = loc_alloc_zero(sizeof(ChildProcess));
(*prs)->pid = pid;
(*prs)->tty = -1;
(*prs)->bcg = c->bcg;
strlcpy((*prs)->service, params->service, sizeof((*prs)->service));
(*prs)->inp_struct = write_process_input(*prs, pipes[0][0]);
(*prs)->out_struct = read_process_output(*prs, pipes[0][0]);
(*prs)->err_struct = read_process_output(*prs, pipes[1][0]);
list_add_first(&(*prs)->link, &prs_list);
if (params->attach) {
taskStop(pid);
taskActivate(pid);
assert(taskIsStopped(pid));
}
else {
taskActivate(pid);
}
semGive(prs_list_lock);
}
}
if (!err) return 0;
errno = err;
return -1;
}
#else
static int start_process_imp(Channel * c, char ** envp, const char * dir, const char * exe, char ** args,
ProcessStartParams * params, int * selfattach, ChildProcess ** prs) {
int err = 0;
int pid = 0;
int p_log[2];
if (pipe(p_log) < 0) return -1;
if (!params->use_terminal) {
int p_inp[2];
int p_out[2];
int p_err[2];
if (pipe(p_inp) < 0 || pipe(p_out) < 0 || pipe(p_err) < 0) err = errno;
if (err == 0 && (p_inp[0] < 3 || p_out[1] < 3 || p_err[1] < 3)) {
int fd0 = p_inp[0];
int fd1 = p_out[1];
int fd2 = p_err[1];
if ((p_inp[0] = dup(p_inp[0])) < 0 ||
(p_out[1] = dup(p_out[1])) < 0 ||
(p_err[1] = dup(p_err[1])) < 0 ||
close(fd0) < 0 ||
close(fd1) < 0 ||
close(fd2) < 0) err = errno;
}
if (!err) {
pid = fork();
if (pid < 0) err = errno;
if (pid == 0) {
int fd = -1;
int fd_flags = 0;
if (close(p_log[0]) < 0 && !err) err = errno;
if (!err && (fd_flags = fcntl(p_log[1], F_GETFD)) < 0) err = errno;
fd_flags |= FD_CLOEXEC;
if (!err && fcntl(p_log[1], F_SETFD, fd_flags) < 0) err = errno;
if (!err && (fd = sysconf(_SC_OPEN_MAX)) < 0) err = errno;
if (!err && dup2(p_inp[0], 0) < 0) err = errno;
if (!err && dup2(p_out[1], 1) < 0) err = errno;
if (!err && dup2(p_err[1], 2) < 0) err = errno;
while (!err && fd > 3 && fd - 1 != p_log[1]) close(--fd);
if (!err && params->attach && context_attach_self() < 0) err = errno;
if (!err && params->dir != NULL && chdir(params->dir) < 0) err = errno;
if (!err) {
if (envp != NULL) environ = envp;
execvp(exe, args);
err = errno;
}
if (write(p_log[1], &err, sizeof(err)) != sizeof(err)) exit(2);
exit(1);
}
}
if (!err) {
*prs = (ChildProcess *)loc_alloc_zero(sizeof(ChildProcess));
(*prs)->pid = pid;
(*prs)->tty = -1;
(*prs)->bcg = c->bcg;
strlcpy((*prs)->service, params->service, sizeof((*prs)->service));
(*prs)->inp_struct = write_process_input(*prs, p_inp[1]);
(*prs)->out_struct = read_process_output(*prs, p_out[0]);
(*prs)->err_struct = read_process_output(*prs, p_err[0]);
list_add_first(&(*prs)->link, &prs_list);
}
else {
if (p_inp[1] >= 0) close(p_inp[1]);
if (p_out[0] >= 0) close(p_out[0]);
if (p_err[0] >= 0) close(p_err[0]);
}
if ((close(p_inp[0]) < 0 || close(p_out[1]) < 0 || close(p_err[1]) < 0) && !err) err = errno;
}
else {
int fd_tty_master = -1;
int fd_tty_slave = -1;
int fd_tty_out = -1;
char * tty_slave_name = NULL;
fd_tty_master = posix_openpt(O_RDWR|O_NOCTTY);
if (fd_tty_master < 0 || grantpt(fd_tty_master) < 0 || unlockpt(fd_tty_master) < 0) err = errno;
if (!err && (tty_slave_name = ptsname(fd_tty_master)) == NULL) err = EINVAL;
if (!err && (fd_tty_slave = open(tty_slave_name, O_RDWR | O_NOCTTY)) < 0) err = errno;
if (!err && (fd_tty_out = dup(fd_tty_master)) < 0) err = errno;
if (!err) {
pid = fork();
if (pid < 0) err = errno;
if (pid == 0) {
int fd = -1;
int fd_flags = 0;
if (close(p_log[0]) < 0 && !err) err = errno;
if (!err && (fd_flags = fcntl(p_log[1], F_GETFD)) < 0) err = errno;
fd_flags |= FD_CLOEXEC;
if (!err && fcntl(p_log[1], F_SETFD, fd_flags) < 0) err = errno;
if (!err && setsid() < 0) err = errno;;
if (!err && (fd = open(tty_slave_name, O_RDWR)) < 0) err = errno;
while (!err && fd < 3) {
int fd0 = fd;
if ((fd = dup(fd)) < 0 || close(fd0)) err = errno;
}
#if defined(TIOCSCTTY)
if (!err && (ioctl(fd, TIOCSCTTY, NULL)) < 0) err = errno;
#endif
if (!err && dup2(fd, 0) < 0) err = errno;
if (!err && dup2(fd, 1) < 0) err = errno;
if (!err && dup2(fd, 2) < 0) err = errno;
if (!err && (fd = sysconf(_SC_OPEN_MAX)) < 0) err = errno;
while (!err && fd > 3 && fd - 1 != p_log[1]) close(--fd);
if (!err && params->attach && context_attach_self() < 0) err = errno;
if (!err && params->dir != NULL && chdir(params->dir) < 0) err = errno;
if (!err) {
if (envp != NULL) environ = envp;
execvp(exe, args);
err = errno;
}
if (write(p_log[1], &err, sizeof(err)) != sizeof(err)) exit(2);
exit(1);
}
}
if (!err) {
*prs = (ChildProcess *)loc_alloc_zero(sizeof(ChildProcess));
(*prs)->pid = pid;
(*prs)->tty = fd_tty_master;
(*prs)->bcg = c->bcg;
strlcpy((*prs)->service, params->service, sizeof((*prs)->service));
(*prs)->inp_struct = write_process_input(*prs, fd_tty_master);
(*prs)->out_struct = read_process_output(*prs, fd_tty_out);
list_add_first(&(*prs)->link, &prs_list);
}
else {
if (fd_tty_master >= 0) close(fd_tty_master);
if (fd_tty_out >= 0) close(fd_tty_out);
}
if (fd_tty_slave >= 0) close(fd_tty_slave);
}
if (close(p_log[1]) < 0 && !err) err = errno;
if (!err) {
int e = 0;
int n = read(p_log[0], &e, sizeof(e));
if (n < 0) err = errno;
else if (n == sizeof(e)) err = e;
}
if (close(p_log[0]) < 0 && !err) err = errno;
*selfattach = 1;
if (!err) return 0;
errno = err;
return -1;
}
#endif
static void waitpid_listener(int pid, int exited, int exit_code, int signal, int event_code, int syscall, void * args) {
if (exited) {
ChildProcess * prs = find_process(pid);
if (prs) {
if (signal != 0) prs->exit_code = -signal;
else prs->exit_code = exit_code;
process_exited(prs);
}
}
}
static void init(void) {
if (init_done) return;
init_done = 1;
#if defined(_WRS_KERNEL)
prs_list_lock = semMCreate(SEM_Q_PRIORITY);
if (prs_list_lock == NULL) check_error(errno);
if (taskCreateHookAdd((FUNCPTR)task_create_hook) != OK) check_error(errno);
if (taskDeleteHookAdd((FUNCPTR)task_delete_hook) != OK) check_error(errno);
#endif /* defined(_WRS_KERNEL) */
add_waitpid_listener(waitpid_listener, NULL);
}
int start_process(Channel * c, ProcessStartParams * params, int * selfattach, ChildProcess ** prs) {
int err = 0;
init();
if (start_process_imp(c, params->envp, params->dir, params->exe,
params->args, params, selfattach, prs) < 0) err = errno;
if (*prs != NULL) {
if (!params->attach || err) add_waitpid_process((*prs)->pid);
strlcpy((*prs)->name, params->exe, sizeof((*prs)->name));
(*prs)->exit_args = params->exit_args;
(*prs)->exit_cb = params->exit_cb;
}
if (!err) {
#if defined(_WIN32) || defined(__CYGWIN__)
#elif defined(_WRS_KERNEL)
#else
if ((*prs)->tty >= 0) {
struct winsize size;
memset(&size, 0, sizeof(struct winsize));
if ((*prs)->tty < 0 || ioctl((*prs)->tty, TIOCGWINSZ, &size) < 0 || size.ws_col <= 0 || size.ws_row <= 0) {
size.ws_col = 80;
size.ws_row = 24;
}
(*prs)->ws_col = size.ws_col;
(*prs)->ws_row = size.ws_row;
}
#endif
}
if (!err) return 0;
errno = err;
return -1;
}
const char * get_process_stream_id(ChildProcess * prs, int stream) {
switch (stream) {
case 0:
if (prs->inp_struct == NULL) return NULL;
return prs->inp_struct->id;
case 1:
if (prs->out_struct == NULL) return NULL;
return prs->out_struct->id;
case 2:
if (prs->err_struct == NULL) return NULL;
return prs->err_struct->id;
}
return NULL;
}
int get_process_tty(ChildProcess * prs) {
return prs->tty;
}
int get_process_pid(ChildProcess * prs) {
return prs->pid;
}
int get_process_out_state(ChildProcess * prs) {
return prs->got_output;
}
int get_process_exit_code(ChildProcess * prs) {
return prs->exit_code;
}
int get_process_tty_win_size(ChildProcess * prs, unsigned * ws_col, unsigned * ws_row) {
*ws_col = prs->ws_col;
*ws_row = prs->ws_row;
return 0;
}
int set_process_tty_win_size(ChildProcess * prs, unsigned ws_col, unsigned ws_row, int * changed) {
if (changed) *changed = 0;
if (prs->ws_col != ws_col || prs->ws_row != ws_row) {
#if defined(_WIN32) || defined(__CYGWIN__)
#elif defined(_WRS_KERNEL)
#else
struct winsize size;
if (prs->tty < 0) {
set_errno(ERR_OTHER, "Process is not connected to sudo-terminal");
return -1;
}
memset(&size, 0, sizeof(size));
size.ws_col = ws_col;
size.ws_row = ws_row;
if (ioctl(prs->tty, TIOCSWINSZ, &size) < 0) return -1;
#endif
prs->ws_col = ws_col;
prs->ws_row = ws_row;
if (changed) *changed = 1;
}
return 0;
}
#if SERVICE_Processes
static void read_start_params(InputStream * inp, const char * nm, void * arg) {
ProcessStartParams * params = (ProcessStartParams *)arg;
if (strcmp(nm, "Attach") == 0) params->attach = json_read_boolean(inp);
else if (strcmp(nm, "AttachChildren") == 0) params->attach_mode |= json_read_boolean(inp) ? CONTEXT_ATTACH_CHILDREN : 0;
else if (strcmp(nm, "StopAtEntry") == 0) params->attach_mode |= json_read_boolean(inp) ? 0 : CONTEXT_ATTACH_NO_STOP;
else if (strcmp(nm, "StopAtMain") == 0) params->attach_mode |= json_read_boolean(inp) ? 0 : CONTEXT_ATTACH_NO_MAIN;
else if (strcmp(nm, "UseTerminal") == 0) params->use_terminal = json_read_boolean(inp);
#if ENABLE_DebugContext
else if (strcmp(nm, "SigDontStop") == 0) read_sigset(inp, &params->sig_dont_stop, &params->set_dont_stop);
else if (strcmp(nm, "SigDontPass") == 0) read_sigset(inp, &params->sig_dont_pass, &params->set_dont_pass);
#endif
else json_skip_object(inp);
}
static void command_start(char * token, Channel * c, void * x) {
int err = 0;
int version = *(int *)x;
int args_len = 0;
int envp_len = 0;
ProcessStartParams params;
int selfattach = 0;
int pending = 0;
ChildProcess * prs = NULL;
Trap trap;
memset(&params, 0, sizeof(params));
if (set_trap(&trap)) {
params.dir = json_read_alloc_string(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
params.exe = json_read_alloc_string(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
params.args = json_read_alloc_string_array(&c->inp, &args_len);
json_test_char(&c->inp, MARKER_EOA);
params.envp = json_read_alloc_string_array(&c->inp, &envp_len);
json_test_char(&c->inp, MARKER_EOA);
if (version > 0 && (json_peek(&c->inp) == '{' || json_peek(&c->inp) == 'n')) {
json_read_struct(&c->inp, read_start_params, &params);
}
else {
params.attach = json_read_boolean(&c->inp);
}
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
if (params.dir != NULL && params.dir[0] == 0) {
loc_free(params.dir);
params.dir = NULL;
}
params.service = PROCESSES[version];
if (err == 0 && start_process(c, &params, &selfattach, &prs) < 0) err = errno;
if (!err && params.attach) {
int mode = params.attach_mode;
AttachDoneArgs * data = (AttachDoneArgs *)loc_alloc_zero(sizeof *data);
data->c = c;
strcpy(data->token, token);
sigset_copy(&data->sig_dont_stop, &params.sig_dont_stop);
sigset_copy(&data->sig_dont_pass, &params.sig_dont_pass);
if (selfattach) mode |= CONTEXT_ATTACH_SELF;
pending = context_attach(prs->pid, start_done, data, mode) == 0;
if (pending) {
channel_lock_with_msg(c, PROCESSES[0]);
}
else {
err = errno;
loc_free(data);
}
}
if (!pending) {
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (err || prs->pid == 0) {
write_stringz(&c->out, "null");
}
else {
write_context(&c->out, prs->pid);
write_stream(&c->out, 0);
}
write_stream(&c->out, MARKER_EOM);
}
clear_trap(&trap);
}
sigset_clear(&params.sig_dont_stop);
sigset_clear(&params.sig_dont_pass);
loc_free(params.dir);
loc_free(params.exe);
loc_free(params.args);
loc_free(params.envp);
if (trap.error) exception(trap.error);
}
static void command_set_win_size(char * token, Channel * c) {
int err = 0;
char id[256];
pid_t pid, parent;
ChildProcess * prs = NULL;
unsigned ws_col;
unsigned ws_row;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
ws_col = json_read_ulong(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
ws_row = json_read_ulong(&c->inp);
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
pid = id2pid(id, &parent);
if (pid == 0 || parent != 0 || (prs = find_process(pid)) == NULL) {
err = ERR_INV_CONTEXT;
}
else if (set_process_tty_win_size(prs, ws_col, ws_row, NULL) < 0) {
err = errno;
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
void ini_processes_service(Protocol * proto) {
int v;
static int vs[] = { 0, 1 };
init();
for (v = 0; v < 2; v++) {
add_command_handler(proto, PROCESSES[v], "getContext", command_get_context);
add_command_handler(proto, PROCESSES[v], "getChildren", command_get_children);
add_command_handler(proto, PROCESSES[v], "terminate", command_terminate);
add_command_handler(proto, PROCESSES[v], "signal", command_signal);
add_command_handler(proto, PROCESSES[v], "getSignalList", command_get_signal_list);
add_command_handler(proto, PROCESSES[v], "getEnvironment", command_get_environment);
add_command_handler2(proto, PROCESSES[v], "start", command_start, vs + v);
#if ENABLE_DebugContext
add_command_handler(proto, PROCESSES[v], "attach", command_attach);
add_command_handler(proto, PROCESSES[v], "detach", command_detach);
add_command_handler(proto, PROCESSES[v], "getSignalMask", command_get_signal_mask);
add_command_handler(proto, PROCESSES[v], "setSignalMask", command_set_signal_mask);
#endif /* ENABLE_DebugContext */
}
add_command_handler(proto, PROCESSES[1], "setWinSize", command_set_win_size);
}
#endif /* SERVICE_Processes */
#endif