blob: a66d41f89e4c499172565136caba4021377d0499 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 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
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.
*/
#include "config.h"
#if SERVICE_Processes
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include "mdep.h"
#include "myalloc.h"
#include "protocol.h"
#include "context.h"
#include "json.h"
#include "exceptions.h"
static const char * PROCESSES = "Processes";
#if defined(WIN32)
# include <direct.h>
#elif defined(_WRS_KERNEL)
# include <symLib.h>
# include <sysSymTbl.h>
#else
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <dirent.h>
#endif
static void write_context(OutputStream * out, char * id, char * dir) {
Context * ctx = NULL;
out->write(out, '{');
#if defined(WIN32)
#elif defined(_WRS_KERNEL)
#else
if (chdir(dir) >= 0) {
int sz;
char fnm[FILE_PATH_SIZE + 1];
json_write_string(out, "CanTerminate");
out->write(out, ':');
json_write_boolean(out, 1);
out->write(out, ',');
if ((sz = readlink("exe", fnm, FILE_PATH_SIZE)) > 0) {
fnm[sz] = 0;
json_write_string(out, "Name");
out->write(out, ':');
json_write_string(out, fnm);
out->write(out, ',');
}
}
#endif
ctx = id2ctx(id);
if (ctx != NULL) {
json_write_string(out, "Attached");
out->write(out, ':');
json_write_boolean(out, 1);
out->write(out, ',');
}
json_write_string(out, "ID");
out->write(out, ':');
json_write_string(out, id);
out->write(out, '}');
}
static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
pid_t pid, parent;
char dir[FILE_PATH_SIZE];
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
pid = id2pid(id, &parent);
write_stringz(out, "R");
write_stringz(out, token);
pid = id2pid(id, &parent);
snprintf(dir, sizeof(dir), "/proc/%d", pid);
if (pid != 0 && parent == 0) {
#if defined(WIN32)
#elif defined(_WRS_KERNEL)
if (TASK_ID_VERIFY(pid) == ERROR) err = ERR_INV_CONTEXT;
#else
struct_stat st;
if (lstat(dir, &st) < 0) err = errno;
else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
#endif
}
write_errno(out, err);
if (err == 0 && pid != 0 && parent == 0) {
write_context(out, id, dir);
out->write(out, 0);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
char id[256];
pid_t parent = 0;
int attached_only;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
attached_only = json_read_boolean(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
if (id[0] != 0) {
write_errno(out, 0);
write_stringz(out, "null");
}
else {
#if defined(WIN32)
#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));
while (1) {
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));
}
out->write(out, '[');
for (i = 0; i < ids_cnt; i++) {
if (!attached_only || context_find_from_pid(ids[i]) != NULL) {
if (cnt > 0) out->write(out, ',');
json_write_string(out, pid2id(ids[i], 0));
cnt++;
}
}
out->write(out, ']');
out->write(out, 0);
#else
DIR * proc = opendir("/proc");
if (proc == NULL) {
write_errno(out, errno);
write_stringz(out, "null");
}
else {
int cnt = 0;
write_errno(out, 0);
out->write(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 || context_find_from_pid(pid) != NULL) {
if (cnt > 0) out->write(out, ',');
json_write_string(out, pid2id(pid, 0));
cnt++;
}
}
}
out->write(out, ']');
out->write(out, 0);
closedir(proc);
}
#endif
}
out->write(out, MARKER_EOM);
}
static void command_attach(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
pid_t pid, parent;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
pid = id2pid(id, &parent);
write_stringz(out, "R");
write_stringz(out, token);
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else if (context_find_from_pid(pid) != NULL) {
err = ERR_ALREADY_ATTACHED;
}
else {
if (context_attach(pid, NULL) < 0) err = errno;
}
write_errno(out, err);
out->write(out, MARKER_EOM);
}
static void command_detach(char * token, InputStream * inp, OutputStream * out) {
// TODO: implement command_detach()
exception(ERR_PROTOCOL);
}
static void command_terminate(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
pid_t pid, parent;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
pid = id2pid(id, &parent);
write_stringz(out, "R");
write_stringz(out, token);
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else {
#if defined(WIN32)
err = ENOSYS;
#elif defined(_WRS_KERNEL)
if (kill(pid, SIGTERM) < 0) err = errno;
#else
if (kill(pid, SIGTERM) < 0) err = errno;
#endif
}
write_errno(out, err);
out->write(out, MARKER_EOM);
}
static void command_signal(char * token, InputStream * inp, OutputStream * out) {
int err = 0;
char id[256];
int signal = 0;
pid_t pid, parent;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
signal = (int)json_read_long(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
pid = id2pid(id, &parent);
write_stringz(out, "R");
write_stringz(out, token);
if (parent != 0) {
err = ERR_INV_CONTEXT;
}
else {
#if defined(WIN32)
err = ENOSYS;
#elif defined(_WRS_KERNEL)
if (kill(pid, signal) < 0) err = errno;
#else
if (kill(pid, signal) < 0) err = errno;
#endif
}
write_errno(out, err);
out->write(out, MARKER_EOM);
}
static void command_get_environment(char * token, InputStream * inp, OutputStream * out) {
char ** p = environ;
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, 0);
out->write(out, '[');
if (p != NULL) {
while (*p != NULL) {
if (p != environ) out->write(out, ',');
json_write_string(out, *p++);
}
}
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void command_start(char * token, InputStream * inp, OutputStream * out) {
Context * ctx = NULL;
int pid = 0;
int err = 0;
char dir[FILE_PATH_SIZE];
char exe[FILE_PATH_SIZE];
char ** args = NULL;
char ** envp = NULL;
int args_len = 0;
int envp_len = 0;
int attach = 0;
Trap trap;
if (set_trap(&trap)) {
json_read_string(inp, dir, sizeof(dir));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
json_read_string(inp, exe, sizeof(exe));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
args = json_read_alloc_string_array(inp, &args_len);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
envp = json_read_alloc_string_array(inp, &envp_len);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
attach = json_read_boolean(inp);
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
if (dir[0] != 0 && chdir(dir) < 0) err = errno;
if (err == 0) {
#if defined(WIN32)
#elif defined(_WRS_KERNEL)
char * ptr;
SYM_TYPE type;
if (symFindByName(sysSymTbl, exe, &ptr, &type) != OK) {
err = errno;
if (err == S_symLib_SYMBOL_NOT_FOUND) err = ERR_SYM_NOT_FOUND;
assert(err != 0);
}
else {
// TODO: arguments, environment
pid = taskCreate("tTcf", 100, 0, 0x4000, (FUNCPTR)ptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (attach) {
taskStop(pid);
taskActivate(pid);
assert(taskIsStopped(pid));
}
else {
taskActivate(pid);
}
}
#else
pid = fork();
if (pid < 0) err = errno;
if (pid == 0) {
if (attach) tkill(getpid(), SIGSTOP);
exit(execve(exe, args, envp));
}
#endif
}
if (attach) {
if (err == 0 && context_attach(pid, &ctx) < 0) err = errno;
if (ctx != NULL && !ctx->stopped) ctx->pending_intercept = 1;
}
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err || pid == 0) {
write_stringz(out, "null");
}
else {
char bf[256];
snprintf(dir, sizeof(dir), "/proc/%d", pid);
write_context(out, strcpy(bf, pid2id(pid, 0)), dir);
out->write(out, 0);
}
out->write(out, MARKER_EOM);
clear_trap(&trap);
}
loc_free(args);
loc_free(envp);
if (trap.error) exception(trap.error);
}
void ini_processes_service(void) {
add_command_handler(PROCESSES, "getContext", command_get_context);
add_command_handler(PROCESSES, "getChildren", command_get_children);
add_command_handler(PROCESSES, "attach", command_attach);
add_command_handler(PROCESSES, "detach", command_detach);
add_command_handler(PROCESSES, "terminate", command_terminate);
add_command_handler(PROCESSES, "signal", command_signal);
add_command_handler(PROCESSES, "getEnvironment", command_get_environment);
add_command_handler(PROCESSES, "start", command_start);
}
#endif