medit/moo/mooterm/mootermpt-unix.c

687 lines
18 KiB
C
Raw Normal View History

2005-06-22 15:13:12 -07:00
/*
2005-07-07 04:06:23 -07:00
* mooterm/mootermpt-unix.c
2005-06-22 15:13:12 -07:00
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define MOOTERM_COMPILATION
#include "mooterm/mootermpt-private.h"
#include "mooterm/mooterm-private.h"
2005-06-22 15:13:12 -07:00
#include "mooterm/pty.h"
#include "mooutils/moomarshals.h"
#include "mooutils/moocompat.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <termios.h>
2005-06-22 15:13:12 -07:00
#ifdef HAVE_POLL_H
2005-07-26 04:12:40 -07:00
# include <poll.h>
2005-06-22 15:13:12 -07:00
#elif HAVE_SYS_POLL_H
2005-07-26 04:12:40 -07:00
# include <sys/poll.h>
2005-06-22 15:13:12 -07:00
#endif
#define TERM_EMULATION "xterm"
#define READ_CHUNK_SIZE 1024
#define WRITE_CHUNK_SIZE 4096
#define POLL_TIME 5
#define POLL_NUM 1
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
#define MOO_TERM_PT_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnix))
#define MOO_TERM_PT_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnixClass))
#define MOO_IS_TERM_PT_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOO_TYPE_TERM_PT_UNIX))
#define MOO_IS_TERM_PT_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_TERM_PT_UNIX))
#define MOO_TERM_PT_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnixClass))
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
typedef struct _MooTermPtUnix MooTermPtUnix;
typedef struct _MooTermPtUnixClass MooTermPtUnixClass;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
struct _MooTermPtUnix {
MooTermPt parent;
2005-06-22 15:13:12 -07:00
GPid child_pid;
int master;
int width;
int height;
gboolean non_block;
2005-06-22 15:13:12 -07:00
GIOChannel *io;
guint io_watch_id;
};
2005-07-07 04:06:23 -07:00
struct _MooTermPtUnixClass {
MooTermPtClass parent_class;
2005-06-22 15:13:12 -07:00
};
2005-07-07 04:06:23 -07:00
static void moo_term_pt_unix_finalize (GObject *object);
2005-06-22 15:13:12 -07:00
2005-07-26 04:12:40 -07:00
const char *get_default_shell (MooTermPt *pt);
static void set_size (MooTermPt *pt,
guint width,
guint height);
static gboolean fork_command (MooTermPt *pt,
const char *cmd,
const char *working_dir,
char **envp);
static gboolean fork_argv (MooTermPt *pt,
char **argv,
const char *working_dir,
char **envp);
static void pt_write (MooTermPt *pt,
const char *string,
gssize len);
static void kill_child (MooTermPt *pt);
static gboolean read_child_out (GIOChannel *source,
GIOCondition condition,
MooTermPtUnix *pt);
static void feed_buffer (MooTermPtUnix *pt,
const char *string,
int len);
static void start_writer (MooTermPt *pt);
static void stop_writer (MooTermPt *pt);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
/* MOO_TYPE_TERM_PT_UNIX */
G_DEFINE_TYPE (MooTermPtUnix, moo_term_pt_unix, MOO_TYPE_TERM_PT)
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static void moo_term_pt_unix_class_init (MooTermPtUnixClass *klass)
2005-06-22 15:13:12 -07:00
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2005-07-07 04:06:23 -07:00
MooTermPtClass *pt_class = MOO_TERM_PT_CLASS (klass);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
gobject_class->finalize = moo_term_pt_unix_finalize;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt_class->set_size = set_size;
pt_class->fork_command = fork_command;
2005-07-26 04:12:40 -07:00
pt_class->fork_argv = fork_argv;
2005-07-07 04:06:23 -07:00
pt_class->write = pt_write;
pt_class->kill_child = kill_child;
2005-07-26 04:12:40 -07:00
pt_class->get_default_shell = get_default_shell;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
static void moo_term_pt_unix_init (MooTermPtUnix *pt)
2005-06-22 15:13:12 -07:00
{
MOO_TERM_PT(pt)->priv->child_alive = FALSE;
2005-07-07 04:06:23 -07:00
pt->child_pid = (GPid)-1;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt->master = -1;
pt->width = 80;
pt->height = 24;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt->non_block = FALSE;
pt->io = NULL;
pt->io_watch_id = 0;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
static void moo_term_pt_unix_finalize (GObject *object)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (object);
kill_child (MOO_TERM_PT (pt));
G_OBJECT_CLASS (moo_term_pt_unix_parent_class)->finalize (object);
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
static void set_size (MooTermPt *pt,
guint width,
guint height)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
MooTermPtUnix *ptu;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
g_return_if_fail (MOO_IS_TERM_PT_UNIX (pt));
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
ptu = MOO_TERM_PT_UNIX (pt);
2005-06-22 15:13:12 -07:00
if (pt->priv->child_alive)
2005-07-07 04:06:23 -07:00
_vte_pty_set_size (ptu->master, width, height);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
ptu->width = width;
ptu->height = height;
2005-06-22 15:13:12 -07:00
}
2005-07-26 04:12:40 -07:00
static gboolean fork_argv (MooTermPt *pt_gen,
char **argv,
2005-06-22 15:13:12 -07:00
const char *working_dir,
char **envp)
{
2005-07-07 04:06:23 -07:00
MooTermPtUnix *pt;
2005-06-22 15:13:12 -07:00
int env_len = 0;
char **new_env;
int status, flags;
int i;
GSource *src;
2005-06-22 15:13:12 -07:00
2005-07-26 04:12:40 -07:00
g_return_val_if_fail (argv != NULL && argv[0] != NULL, FALSE);
2005-07-07 04:06:23 -07:00
g_return_val_if_fail (MOO_IS_TERM_PT_UNIX (pt_gen), FALSE);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt = MOO_TERM_PT_UNIX (pt_gen);
2005-06-22 15:13:12 -07:00
g_return_val_if_fail (!pt_gen->priv->child_alive, FALSE);
2005-06-22 15:13:12 -07:00
if (envp)
{
char **e;
for (e = envp; *e != NULL; ++e)
++env_len;
}
new_env = g_new (char*, env_len + 2);
for (i = 0; i < env_len; ++i)
new_env[i] = g_strdup (envp[i]);
new_env[env_len] = g_strdup ("TERM=" TERM_EMULATION);
new_env[env_len + 1] = NULL;
2005-07-07 04:06:23 -07:00
pt->master = _vte_pty_open (&pt->child_pid, new_env,
2005-06-22 15:13:12 -07:00
argv[0],
argv,
working_dir,
2005-07-07 04:06:23 -07:00
pt->width, pt->height,
2005-06-22 15:13:12 -07:00
FALSE, FALSE, FALSE);
g_strfreev (new_env);
2005-07-07 04:06:23 -07:00
if (pt->master == -1)
2005-06-22 15:13:12 -07:00
{
g_critical ("%s: could not fork child", G_STRLOC);
return FALSE;
}
else
{
2005-07-07 04:06:23 -07:00
g_message ("%s: forked child pid %d", G_STRLOC, pt->child_pid);
2005-06-22 15:13:12 -07:00
}
if (waitpid (-1, &status, WNOHANG) == -1)
g_critical ("%s: error in waitpid", G_STRLOC);
2005-07-07 04:06:23 -07:00
if ((flags = fcntl (pt->master, F_GETFL)) < 0)
2005-06-22 15:13:12 -07:00
g_critical ("%s: F_GETFL on master", G_STRLOC);
2005-07-07 04:06:23 -07:00
else if (-1 == fcntl (pt->master, F_SETFL, O_NONBLOCK | flags))
2005-06-22 15:13:12 -07:00
g_critical ("%s: F_SETFL on master", G_STRLOC);
else
2005-07-07 04:06:23 -07:00
pt->non_block = TRUE;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt->io = g_io_channel_unix_new (pt->master);
g_return_val_if_fail (pt->io != NULL, FALSE);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
g_io_channel_set_encoding (pt->io, NULL, NULL);
g_io_channel_set_buffered (pt->io, FALSE);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt->io_watch_id = g_io_add_watch (pt->io,
2005-06-22 15:13:12 -07:00
G_IO_IN | G_IO_PRI | G_IO_HUP,
(GIOFunc) read_child_out,
2005-07-07 04:06:23 -07:00
pt);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
src = g_main_context_find_source_by_id (NULL, pt->io_watch_id);
if (src)
2005-07-07 04:06:23 -07:00
g_source_set_priority (src, PT_READER_PRIORITY);
else
g_warning ("%s: could not find io_watch_id source", G_STRLOC);
2005-06-22 15:13:12 -07:00
pt_gen->priv->child_alive = TRUE;
2005-06-22 15:13:12 -07:00
return TRUE;
}
2005-07-26 04:12:40 -07:00
static gboolean fork_command (MooTermPt *pt_gen,
const char *cmd,
const char *working_dir,
char **envp)
{
MooTermPtUnix *pt;
int argv_len;
char **argv = NULL;
GError *err = NULL;
gboolean result;
g_return_val_if_fail (cmd != NULL && cmd[0] != 0, FALSE);
g_return_val_if_fail (MOO_IS_TERM_PT_UNIX (pt_gen), FALSE);
pt = MOO_TERM_PT_UNIX (pt_gen);
g_return_val_if_fail (!pt_gen->priv->child_alive, FALSE);
if (!g_shell_parse_argv (cmd, &argv_len, &argv, &err))
{
g_critical ("%s: could not parse command line", G_STRLOC);
if (err != NULL)
{
g_critical ("%s: %s", G_STRLOC, err->message);
g_error_free (err);
}
if (argv != NULL)
g_strfreev (argv);
return FALSE;
}
result = fork_argv (pt_gen, argv, working_dir, envp);
g_strfreev (argv);
return result;
}
2005-07-07 04:06:23 -07:00
static void kill_child (MooTermPt *pt_gen)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
if (pt->io_watch_id)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
g_source_remove (pt->io_watch_id);
pt->io_watch_id = 0;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
if (pt->io)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
pt_write (MOO_TERM_PT (pt), "\4", 1);
g_io_channel_shutdown (pt->io, TRUE, NULL);
g_io_channel_unref (pt->io);
pt->io = NULL;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
stop_writer (pt_gen);
pt_flush_pending_write (pt_gen);
2005-07-07 04:06:23 -07:00
if (pt->master != -1)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
_vte_pty_close (pt->master);
pt->master = -1;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
pt->non_block = FALSE;
2005-07-07 04:06:23 -07:00
pt->child_pid = (GPid)-1;
2005-06-22 15:13:12 -07:00
if (pt_gen->priv->child_alive)
2005-06-22 15:13:12 -07:00
{
pt_gen->priv->child_alive = FALSE;
2005-07-07 04:06:23 -07:00
g_signal_emit_by_name (pt, "child-died");
2005-06-22 15:13:12 -07:00
}
}
static gboolean read_child_out (G_GNUC_UNUSED GIOChannel *source,
GIOCondition condition,
2005-07-07 04:06:23 -07:00
MooTermPtUnix *pt)
2005-06-22 15:13:12 -07:00
{
gboolean error_occured = FALSE;
int error_no = 0;
char buf[READ_CHUNK_SIZE];
2005-06-22 15:13:12 -07:00
int current = 0;
guint again = POLL_NUM;
if (condition & G_IO_HUP)
{
g_message ("%s: G_IO_HUP", G_STRLOC);
error_no = errno;
goto error;
}
else if (condition & G_IO_ERR)
{
g_message ("%s: G_IO_ERR", G_STRLOC);
error_no = errno;
goto error;
}
g_assert (condition & (G_IO_IN | G_IO_PRI));
2005-07-07 04:06:23 -07:00
if (pt->non_block)
{
int r = read (pt->master, buf, READ_CHUNK_SIZE);
switch (r)
{
case -1:
if (errno != EAGAIN && errno != EINTR)
{
error_occured = TRUE;
error_no = errno;
goto error;
}
break;
case 0:
break;
default:
#if 0
{
char *s = _moo_term_nice_bytes (buf, r);
g_print ("got '%s'\n", s);
g_free (s);
}
#endif
2005-07-07 04:06:23 -07:00
feed_buffer (pt, buf, r);
break;
}
return TRUE;
}
while (again && !error_occured && current < READ_CHUNK_SIZE)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
struct pollfd fd = {pt->master, POLLIN | POLLPRI, 0};
2005-06-22 15:13:12 -07:00
int res = poll (&fd, 1, POLL_TIME);
switch (res)
{
case 0:
--again;
break;
case 1:
if (fd.revents & (POLLNVAL | POLLERR | POLLHUP))
{
again = 0;
if (errno != EAGAIN && errno != EINTR)
{
error_occured = TRUE;
error_no = errno;
}
}
else if (fd.revents & (POLLIN | POLLPRI))
{
2005-07-07 04:06:23 -07:00
int r = read (pt->master, buf + current, 1);
2005-06-22 15:13:12 -07:00
switch (r)
{
case -1:
error_occured = TRUE;
error_no = errno;
break;
case 0:
--again;
break;
case 1:
++current;
break;
default:
g_assert_not_reached();
}
}
2005-06-22 15:13:12 -07:00
break;
case -1:
again = 0;
if (errno != EAGAIN && errno != EINTR)
{
error_occured = TRUE;
error_no = errno;
}
break;
default:
g_assert_not_reached();
}
}
if (current > 0)
2005-07-07 04:06:23 -07:00
feed_buffer (pt, buf, current);
2005-06-22 15:13:12 -07:00
if (error_occured)
goto error;
return TRUE;
error:
if (error_occured)
g_message ("error in %s", G_STRLOC);
if (error_no)
g_message ("%s: %s", G_STRLOC, g_strerror (error_no));
2005-07-07 04:06:23 -07:00
if (pt->io)
2005-06-22 15:13:12 -07:00
{
2005-07-07 04:06:23 -07:00
_vte_pty_close (pt->master);
pt->master = -1;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
g_io_channel_shutdown (pt->io, TRUE, NULL);
g_io_channel_unref (pt->io);
pt->io = NULL;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
pt->io_watch_id = 0;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
kill_child (MOO_TERM_PT (pt));
2005-06-22 15:13:12 -07:00
return FALSE;
}
2005-07-07 04:06:23 -07:00
static void feed_buffer (MooTermPtUnix *pt,
2005-06-22 15:13:12 -07:00
const char *string,
int len)
2005-06-22 15:13:12 -07:00
{
moo_term_feed (MOO_TERM_PT(pt)->priv->term,
string, len);
2005-06-22 15:13:12 -07:00
}
/* writes given data to file, returns TRUE on successful write,
FALSE when could not write al teh data, puts start of leftover
to string, length of it to len, and fills err in case of error */
2005-07-07 04:06:23 -07:00
static gboolean do_write (MooTermPt *pt_gen,
const char **string,
guint *plen,
int *err)
2005-06-22 15:13:12 -07:00
{
int written;
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
g_return_val_if_fail (pt->io != NULL, FALSE);
written = write (pt->master, *string,
*plen > WRITE_CHUNK_SIZE ? WRITE_CHUNK_SIZE : *plen);
2005-06-22 15:13:12 -07:00
if (written == -1)
{
*err = errno;
switch (errno)
{
case EAGAIN:
case EINTR:
g_warning ("%s", G_STRLOC);
return TRUE;
default:
return FALSE;
}
}
else
2005-06-22 15:13:12 -07:00
{
*string += written;
*plen -= written;
2005-06-22 15:13:12 -07:00
if (!*plen)
*string = NULL;
return TRUE;
}
}
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static void append (MooTermPt *pt,
const char *data,
guint len)
{
GByteArray *ar = g_byte_array_sized_new (len);
g_byte_array_append (ar, (const guint8*)data, len);
2005-07-07 04:06:23 -07:00
g_queue_push_tail (pt->priv->pending_write, ar);
}
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static gboolean write_cb (MooTermPt *pt)
{
2005-07-07 04:06:23 -07:00
pt_write (pt, NULL, 0);
return TRUE;
}
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static void start_writer (MooTermPt *pt)
{
2005-07-07 04:06:23 -07:00
if (!pt->priv->pending_write_id)
pt->priv->pending_write_id =
g_idle_add_full (PT_WRITER_PRIORITY,
(GSourceFunc) write_cb,
2005-07-07 04:06:23 -07:00
pt, NULL);
}
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static void stop_writer (MooTermPt *pt)
{
2005-07-07 04:06:23 -07:00
if (pt->priv->pending_write_id)
{
2005-07-07 04:06:23 -07:00
g_source_remove (pt->priv->pending_write_id);
pt->priv->pending_write_id = 0;
}
}
2005-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
static void pt_write (MooTermPt *pt,
const char *data,
int data_len)
{
g_return_if_fail (data == NULL || data_len != 0);
g_return_if_fail (pt->priv->child_alive);
2005-07-07 04:06:23 -07:00
while (data || !g_queue_is_empty (pt->priv->pending_write))
{
int err = 0;
const char *string;
guint len;
GByteArray *freeme = NULL;
2005-07-07 04:06:23 -07:00
if (!g_queue_is_empty (pt->priv->pending_write))
{
if (data)
{
2005-07-07 04:06:23 -07:00
append (pt, data, data_len > 0 ? (guint)data_len : strlen (data));
data = NULL;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
freeme = g_queue_peek_head (pt->priv->pending_write);
2005-07-19 07:01:41 -07:00
string = (const char *) freeme->data;
len = freeme->len;
2005-06-22 15:13:12 -07:00
}
else
2005-06-22 15:13:12 -07:00
{
string = data;
len = data_len > 0 ? (guint)data_len : strlen (data);
data = NULL;
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
if (do_write (pt, &string, &len, &err))
{
if (len)
{
if (freeme)
{
memmove (freeme->data, freeme->data + (freeme->len - len), len);
g_byte_array_set_size (freeme, len);
}
else
{
2005-07-07 04:06:23 -07:00
append (pt, string, len);
}
break;
}
else if (freeme)
{
g_byte_array_free (freeme, TRUE);
2005-07-07 04:06:23 -07:00
g_queue_pop_head (pt->priv->pending_write);
}
}
else
{
g_message ("%s: stopping writing to child", G_STRLOC);
if (err)
g_message ("%s: %s", G_STRLOC, g_strerror (err));
2005-07-07 04:06:23 -07:00
kill_child (pt);
}
2005-06-22 15:13:12 -07:00
}
2005-07-07 04:06:23 -07:00
if (!g_queue_is_empty (pt->priv->pending_write))
start_writer (pt);
else
2005-07-07 04:06:23 -07:00
stop_writer (pt);
2005-06-22 15:13:12 -07:00
}
char moo_term_pt_get_erase_char (MooTermPt *pt_gen)
{
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
struct termios tio;
g_return_val_if_fail (pt->master != -1, 0);
if (!tcgetattr (pt->master, &tio))
{
return tio.c_cc[VERASE];
}
else
{
g_warning ("%s: %s", G_STRLOC,
g_strerror (errno));
2005-07-26 04:12:40 -07:00
return 127;
}
}
void moo_term_pt_send_intr (MooTermPt *pt)
{
g_return_if_fail (pt->priv->child_alive);
pt_flush_pending_write (pt);
pt_write (pt, "\003", 1);
}
2005-07-26 04:12:40 -07:00
/* TODO: it should be in glib */
const char *get_default_shell (G_GNUC_UNUSED MooTermPt *pt)
{
const char *shell;
2005-07-29 04:51:36 -07:00
shell = g_getenv ("SHELL");
2005-07-26 04:12:40 -07:00
if (!shell) shell = "/bin/sh";
return shell;
}