411 lines
12 KiB
C
Executable File
411 lines
12 KiB
C
Executable File
/*
|
|
* Copyright (c) 2013-2016, Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification, are
|
|
* permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software without specific
|
|
* prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include "guest_script.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include "guest_limits.h"
|
|
#include "guest_types.h"
|
|
#include "guest_debug.h"
|
|
#include "guest_selobj.h"
|
|
#include "guest_utils.h"
|
|
#include "guest_child_death.h"
|
|
|
|
#define GUEST_SCRIPT_SETUP_FAILURE -65535
|
|
|
|
typedef struct {
|
|
bool inuse;
|
|
int pid;
|
|
int fd;
|
|
int log_end_ptr;
|
|
char log_msg[256];
|
|
GuestScriptIdT script_id;
|
|
GuestScriptCallbackT callback;
|
|
} GuestScriptDataT;
|
|
|
|
static GuestScriptDataT _scripts[GUEST_CHILD_PROCESS_MAX];
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Abort
|
|
// ====================
|
|
void guest_script_abort( GuestScriptIdT script_id )
|
|
{
|
|
int result;
|
|
GuestScriptDataT* entry;
|
|
GuestErrorT error;
|
|
|
|
if (GUEST_SCRIPT_ID_INVALID == script_id)
|
|
return;
|
|
|
|
if (GUEST_CHILD_PROCESS_MAX <= script_id)
|
|
return;
|
|
|
|
entry = &(_scripts[script_id]);
|
|
|
|
if (entry->inuse)
|
|
{
|
|
if (-1 != entry->pid)
|
|
{
|
|
error = guest_child_death_deregister(entry->pid);
|
|
if (GUEST_OKAY != error)
|
|
{
|
|
DPRINTFE("Failed to deregister for child death %i, error=%s.",
|
|
entry->pid, guest_error_str(error));
|
|
}
|
|
|
|
result = kill(entry->pid, SIGKILL);
|
|
if (0 > result)
|
|
{
|
|
if (ESRCH == errno)
|
|
{
|
|
DPRINTFV("Script pid (%i) not running.", entry->pid);
|
|
} else {
|
|
DPRINTFE("Failed to send kill signal to script pid %i, "
|
|
"error=%s.", entry->pid, strerror(errno));
|
|
}
|
|
} else {
|
|
DPRINTFD("Script pid (%i) killed.", entry->pid);
|
|
}
|
|
}
|
|
|
|
if (-1 != entry->fd)
|
|
{
|
|
error = guest_selobj_deregister(entry->fd);
|
|
if (GUEST_OKAY != error)
|
|
{
|
|
DPRINTFE("Failed to deregister selection object %i, error=%s.",
|
|
entry->fd, guest_error_str(error));
|
|
}
|
|
|
|
close(entry->fd);
|
|
}
|
|
|
|
memset(entry, 0, sizeof(GuestScriptDataT));
|
|
entry->pid = -1;
|
|
entry->fd = -1;
|
|
entry->script_id = GUEST_SCRIPT_ID_INVALID;
|
|
}
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Dispatch
|
|
// =======================
|
|
static void guest_script_dispatch( int selobj )
|
|
{
|
|
int bytes_avail;
|
|
int result;
|
|
GuestScriptDataT* entry;
|
|
|
|
unsigned int script_i;
|
|
for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i)
|
|
{
|
|
entry = &(_scripts[script_i]);
|
|
if (entry->inuse)
|
|
if (selobj == entry->fd)
|
|
break;
|
|
}
|
|
|
|
if (GUEST_CHILD_PROCESS_MAX <= script_i)
|
|
return;
|
|
|
|
bytes_avail = sizeof(entry->log_msg) - entry->log_end_ptr;
|
|
|
|
result = read(selobj, &(entry->log_msg[entry->log_end_ptr]), bytes_avail);
|
|
if (0 > result)
|
|
{
|
|
if (EINTR == errno) {
|
|
DPRINTFD("Interrupted on read, error=%s.", strerror(errno));
|
|
return;
|
|
|
|
} else {
|
|
DPRINTFE("Failed to read, error=%s.", strerror(errno));
|
|
return;
|
|
}
|
|
} else if (0 == result) {
|
|
DPRINTFD("No message received.");
|
|
return;
|
|
|
|
} else {
|
|
DPRINTFD("Received message, msg_size=%i.", result);
|
|
entry->log_end_ptr += result;
|
|
}
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Callback
|
|
// =======================
|
|
static void guest_script_callback( pid_t pid, int exit_code )
|
|
{
|
|
GuestScriptDataT* entry;
|
|
|
|
unsigned int script_i;
|
|
for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i)
|
|
{
|
|
entry = &(_scripts[script_i]);
|
|
if (entry->inuse)
|
|
if ((int) pid == entry->pid)
|
|
break;
|
|
}
|
|
|
|
if (GUEST_CHILD_PROCESS_MAX <= script_i)
|
|
return;
|
|
|
|
DPRINTFD("PID %i exited with %i", (int) pid, exit_code);
|
|
|
|
if (NULL != entry->callback)
|
|
entry->callback(entry->script_id, exit_code, entry->log_msg);
|
|
|
|
guest_script_abort(entry->script_id);
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Invoke
|
|
// =====================
|
|
GuestErrorT guest_script_invoke(
|
|
char script[], char* script_argv[], GuestScriptCallbackT callback,
|
|
GuestScriptIdT* script_id )
|
|
{
|
|
int fd[2];
|
|
pid_t pid;
|
|
struct stat stat_data;
|
|
char* script_name = guest_utils_basename(script);
|
|
char* script_exec = script;
|
|
int result;
|
|
GuestScriptDataT* entry;
|
|
GuestSelObjCallbacksT callbacks;
|
|
GuestErrorT error;
|
|
|
|
*script_id = GUEST_SCRIPT_ID_INVALID;
|
|
|
|
unsigned int script_i;
|
|
for (script_i=1; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i)
|
|
{
|
|
entry = &(_scripts[script_i]);
|
|
if (!entry->inuse)
|
|
break;
|
|
}
|
|
|
|
if (GUEST_CHILD_PROCESS_MAX <= script_i)
|
|
{
|
|
DPRINTFE("Failed to allocate script data.");
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
memset(entry, 0, sizeof(GuestScriptDataT));
|
|
entry->script_id = script_i;
|
|
entry->callback = callback;
|
|
entry->pid = -1;
|
|
entry->fd = -1;
|
|
|
|
result = access(script_exec, F_OK | X_OK);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Script %s access failed, error=%s.", script_exec,
|
|
strerror(errno));
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
result = stat(script_exec, &stat_data);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Script %s stat failed, error=%s.", script_exec,
|
|
strerror( errno ) );
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
if (0 >= stat_data.st_size)
|
|
{
|
|
DPRINTFE("Script %s has zero size.", script_exec);
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
result = pipe(fd);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Script %s pipe creation failed, error=%s.", script_exec,
|
|
strerror(errno));
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
result = fcntl(fd[0], F_SETFL, O_NONBLOCK);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Script %s pipe failed to make read end non-blocking, "
|
|
"error=%s.", script_exec, strerror(errno));
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
return GUEST_FAILED;
|
|
}
|
|
|
|
pid = fork();
|
|
if (0 > pid)
|
|
{
|
|
DPRINTFE("Failed to fork process for script %s, error=%s.",
|
|
script_exec, strerror(errno));
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
return GUEST_FAILED;
|
|
|
|
} else if (0 == pid) {
|
|
// Child process.
|
|
struct rlimit file_limits;
|
|
|
|
close(fd[0]); // close read end of pipe
|
|
|
|
result = setpgid(0, 0);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Failed to set process group id for script %s, "
|
|
"error=%s.", script_exec, strerror( errno ) );
|
|
exit(GUEST_SCRIPT_SETUP_FAILURE);
|
|
}
|
|
|
|
result = getrlimit(RLIMIT_NOFILE, &file_limits);
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Failed to get file limits for script %s, error=%s.",
|
|
script_exec, strerror(errno));
|
|
exit(GUEST_SCRIPT_SETUP_FAILURE);
|
|
}
|
|
|
|
unsigned int fd_i;
|
|
for (fd_i=0; fd_i < file_limits.rlim_cur; ++fd_i)
|
|
if (fd_i != fd[1])
|
|
close(fd_i);
|
|
|
|
result = dup2(fd[1], 1); // make stdout into writable end of pipe
|
|
if (0 > result)
|
|
{
|
|
DPRINTFE("Failed to make stdout into writable end of pipe for "
|
|
"script %s, error=%s.", script_exec, strerror(errno));
|
|
exit(GUEST_SCRIPT_SETUP_FAILURE);
|
|
}
|
|
|
|
result = execv(script_exec, (char**) script_argv);
|
|
if (0 > result)
|
|
DPRINTFE("Failed to exec command for script %s, error=%s.",
|
|
script_exec, strerror(errno));
|
|
|
|
exit(GUEST_SCRIPT_SETUP_FAILURE);
|
|
|
|
} else {
|
|
// Parent process.
|
|
close(fd[1]); // close write end of pipe
|
|
entry->pid = (int) pid;
|
|
entry->fd = fd[0];
|
|
entry->inuse = true;
|
|
|
|
DPRINTFD("Child process %i created for script %s, script_id=%i.",
|
|
entry->pid, script_name, entry->script_id);
|
|
|
|
error = guest_child_death_register(pid, guest_script_callback);
|
|
if (GUEST_OKAY != error)
|
|
{
|
|
DPRINTFE("Failed to register for child death %i, error=%s.",
|
|
entry->pid, guest_error_str(error));
|
|
guest_script_abort(entry->script_id);
|
|
return error;
|
|
}
|
|
|
|
memset(&callbacks, 0, sizeof(callbacks));
|
|
callbacks.read_callback = guest_script_dispatch;
|
|
|
|
error = guest_selobj_register(entry->fd, &callbacks);
|
|
if (GUEST_OKAY != error)
|
|
{
|
|
DPRINTFE("Failed to register selection object %i, error=%s.",
|
|
entry->fd, guest_error_str(error));
|
|
guest_script_abort(entry->script_id);
|
|
return error;
|
|
}
|
|
|
|
*script_id = entry->script_id;
|
|
}
|
|
|
|
return GUEST_OKAY;
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Initialize
|
|
// =========================
|
|
GuestErrorT guest_script_initialize( void )
|
|
{
|
|
GuestScriptDataT* entry;
|
|
|
|
memset(_scripts, 0, sizeof(_scripts));
|
|
|
|
unsigned int script_i;
|
|
for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i)
|
|
{
|
|
entry = &(_scripts[script_i]);
|
|
|
|
entry->pid = -1;
|
|
entry->fd = -1;
|
|
entry->script_id = GUEST_SCRIPT_ID_INVALID;
|
|
}
|
|
|
|
return GUEST_OKAY;
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ****************************************************************************
|
|
// Guest Script - Finalize
|
|
// =======================
|
|
GuestErrorT guest_script_finalize( void )
|
|
{
|
|
GuestScriptDataT* entry;
|
|
|
|
unsigned int script_i;
|
|
for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i)
|
|
{
|
|
entry = &(_scripts[script_i]);
|
|
if (entry->inuse)
|
|
guest_script_abort(entry->script_id);
|
|
}
|
|
|
|
memset(_scripts, 0, sizeof(_scripts));
|
|
return GUEST_OKAY;
|
|
}
|
|
// ****************************************************************************
|