Exemple #1
0
int child_t::terminate() {
	signal_block(SIGCHLD);
	if ( pid > 0 ) {
		kill(pid, SIGTERM);
		printf("waiting for child termination...\n");
		signal_unblock(SIGCHLD);
		sleep(1);
	}
	signal_unblock(SIGCHLD);
	this->child_postmortem();
	return ( status != CLD_RUNNING );
}
Exemple #2
0
void
nextconfig (const char *file)
{
#ifndef IPV6
  struct servent *sp;
#endif
  struct servtab *sep, **sepp;
  struct passwd *pwd;
  FILE *fconfig;
  SIGSTATUS sigstatus;

  size_t line = 0;

  fconfig = setconfig (file);
  if (!fconfig)
    {
      syslog (LOG_ERR, "%s: %m", file);
      return;
    }
  while ((sep = getconfigent (fconfig, file, &line)))
    {
      if ((pwd = getpwnam (sep->se_user)) == NULL)
	{
	  syslog (LOG_ERR, "%s/%s: No such user '%s', service ignored",
		  sep->se_service, sep->se_proto, sep->se_user);
	  continue;
	}
      if (ISMUX (sep))
	{
	  sep->se_fd = -1;
	  sep->se_checked = 1;
	  enter (sep);
	}
      else
	expand_enter (sep);
      freeconfig (sep);
    }
  endconfig (fconfig);
  /*
   * Purge anything not looked at above.
   */
  signal_block (&sigstatus);
  sepp = &servtab;
  while ((sep = *sepp))
    {
      if (sep->se_checked)
	{
	  sepp = &sep->se_next;
	  continue;
	}
      *sepp = sep->se_next;
      if (sep->se_fd >= 0)
	close_sep (sep);
      if (debug)
	print_service ("FREE", sep);
      freeconfig (sep);
      free (sep);
    }
  signal_unblock (&sigstatus);
}
Exemple #3
0
static void internal_exec_helper( const wchar_t *def, 
								 int block_type,
								 io_data_t *io )
{
	io_data_t *io_internal = io_transmogrify( io );
	int is_block_old=is_block;
	is_block=1;
	
	/*
	  Did the transmogrification fail - if so, set error status and return
	*/
	if( io && !io_internal )
	{
		proc_set_last_status( STATUS_EXEC_FAIL );
		return;
	}
	
	signal_unblock();
	
	eval( def, io_internal, block_type );		
	
	signal_block();
	
	io_untransmogrify( io, io_internal );
	job_reap( 0 );
	is_block=is_block_old;
}
static int check_signals(void)
{
	int sig = 0;

	signal_block(sigmask);
	if(end_signal){
#ifdef DEBUG
		write_log("LM(P) SIGTERM receive(data_output_process)\n");
#endif
		sig |= DETECT_END; /* SIGTERM */
	}

	if(last_signal){
#ifdef DEBUG
		write_log("LM(P) SIGUSR1 receive(data_output_process)\n");
#endif
		/* data read until pipe empty */
		fcntl(DATA_PATH, F_SETFL, fcntl(DATA_PATH, F_GETFL, 0) | O_NONBLOCK);
		last_signal = 0;
		sig |= DETECT_LAST; /* SIGUSR1 */
	}

	if(start_signal){
#ifdef DEBUG
		write_log("LM(P) SIGUSR2 receive(data_output_process)\n");
#endif
		sig |= DETECT_START;
	}
	signal_unblock(sigmask);

	return sig;
}
Exemple #5
0
// DESTRUCTOR
child_t::~child_t() {
	child[thischild] = child[noc];
	noc--;
	signal_block(SIGCHLD);
	if (pid >0) { kill(pid, SIGKILL); }	// FIXME: this will cause an unmatched SIGCHLD
	signal_unblock(SIGCHLD);
}
Exemple #6
0
int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain)
{
    bool ok=true;

    if (p)
    {
        ok = (0 == set_child_group(j, p, 1));
    }

    if (ok)
    {
        ok = (0 == handle_child_io(io_chain));
        if (p != 0 && ! ok)
        {
            exit_without_destructors(1);
        }
    }

    /* Set the handling for job control signals back to the default.  */
    if (ok)
    {
        signal_reset_handlers();
    }

    /* Remove all signal blocks */
    signal_unblock();

    return ok ? 0 : -1;
}
Exemple #7
0
/**
   Initialize a new child process. This should be called right away
   after forking in the child process. If job control is enabled for
   this job, the process is put in the process group of the job, all
   signal handlers are reset, signals are unblocked (this function may
   only be called inside the exec function, which blocks all signals),
   and all IO redirections and other file descriptor actions are
   performed.

   \param j the job to set up the IO for
   \param p the child process to set up

   \return 0 on sucess, -1 on failiure. When this function returns,
   signals are always unblocked. On failiure, signal handlers, io
   redirections and process group of the process is undefined.
*/
static int setup_child_process( job_t *j, process_t *p )
{
	int res=0;
	
	if( p )
	{
		res = set_child_group( j, p, 1 );
	}
	
	if( !res )	
	{
		res = handle_child_io( j->io );
		if( p != 0 && res )
		{
			exit( 1 );
		}
	}
	
	/* Set the handling for job control signals back to the default.  */
	if( !res )
	{
		signal_reset_handlers();
	}
	
	/* Remove all signal blocks */
	signal_unblock();	
	
	return res;
	
}
Exemple #8
0
bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid)
{
    wcstring cmd;
    pid_t pid = 0;
    while (cmd.empty())
    {
        wcstring name;
        if (! dir || ! wreaddir(dir, name))
            break;

        if (!iswnumeric(name.c_str()))
            continue;

        wcstring path = wcstring(L"/proc/") + name;
        struct stat buf;
        if (wstat(path, &buf))
            continue;

        if (buf.st_uid != getuid())
            continue;

        /* remember the pid */
        pid = fish_wcstoi(name.c_str(), NULL, 10);

        /* the 'cmdline' file exists, it should contain the commandline */
        FILE *cmdfile;
        if ((cmdfile=wfopen(path + L"/cmdline", "r")))
        {
            wcstring full_command_line;
            signal_block();
            fgetws2(&full_command_line, cmdfile);
            signal_unblock();

            /* The command line needs to be escaped */
            cmd = tok_first(full_command_line.c_str());
        }
#ifdef SunOS
        else if ((cmdfile=wfopen(path + L"/psinfo", "r")))
        {
            psinfo_t info;
            if (fread(&info, sizeof(info), 1, cmdfile))
            {
                /* The filename is unescaped */
                cmd = str2wcstring(info.pr_fname);
            }
        }
#endif
        if (cmdfile)
            fclose(cmdfile);
    }

    bool result = ! cmd.empty();
    if (result)
    {
        *out_str = cmd;
        *out_pid = pid;
    }
    return result;
}
Exemple #9
0
/// Returns control of the terminal to the shell, and saves the terminal attribute state to the job,
/// so that we can restore the terminal ownership to the job at a later time.
static bool terminal_return_from_job(job_t *j) {
    errno = 0;
    if (j->pgid == 0) {
        debug(2, "terminal_return_from_job() returning early due to no process group");
        return true;
    }

    signal_block();
    if (tcsetpgrp(STDIN_FILENO, getpgrp()) == -1) {
        if (errno == ENOTTY) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetpgrp");
        signal_unblock();
        return false;
    }

    // Save jobs terminal modes.
    if (tcgetattr(STDIN_FILENO, &j->tmodes)) {
        if (errno == EIO) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcgetattr");
        signal_unblock();
        return false;
    }

// Disabling this per
// https://github.com/adityagodbole/fish-shell/commit/9d229cd18c3e5c25a8bd37e9ddd3b67ddc2d1b72 On
// Linux, 'cd . ; ftp' prevents you from typing into the ftp prompt. See
// https://github.com/fish-shell/fish-shell/issues/121
#if 0
    // Restore the shell's terminal modes.
    if (tcsetattr(STDIN_FILENO, TCSADRAIN, &shell_modes) == -1) {
        if (errno == EIO) redirect_tty_output();
        debug(1, _(L"Could not return shell to foreground"));
        wperror(L"tcsetattr");
        return false;
    }
#endif

    signal_unblock();
    return true;
}
Exemple #10
0
void parser_t::job_promote(job_t *job)
{
    signal_block();

    job_list_t::iterator loc = std::find(my_job_list.begin(), my_job_list.end(), job);
    assert(loc != my_job_list.end());

    /* Move the job to the beginning */
    my_job_list.splice(my_job_list.begin(), my_job_list, loc);
    signal_unblock();
}
Exemple #11
0
int child_t::parent_postfork_init() {	// this is only executed by the parent
	// close the pipes other ends.
	inp.close_read();
	outp.close_write();
	errp.close_write();
	//fprintf(stderr, "parent: child stdin is connected to fd%d for %d\n", inp.getfd(), inp.getchecks());
	//fprintf(stderr, "parent: child stdout is connected to fd%d for %d\n", outp.getfd(), outp.getchecks());
	//fprintf(stderr, "parent: child stderr is connected to fd%d for %d\n", errp.getfd(), errp.getchecks());
	status = CLD_RUNNING;
	signal_unblock(SIGCHLD);	// UNBLOCK
	return 0;	
}
Exemple #12
0
void event_remove(const event_t &criterion)
{
    event_list_t new_list;

    if (debug_level >= 3)
    {
        wcstring desc = event_desc_compact(criterion);
        debug(3, "unregister: %ls\n", desc.c_str());
    }

    /*
      Because of concurrency issues (env_remove could remove an event
      that is currently being executed), env_remove does not actually
      free any events - instead it simply moves all events that should
      be removed from the event list to the killme list, and the ones
      that shouldn't be killed to new_list, and then drops the empty
      events-list.
    */

    if (events.empty())
        return;

    for (size_t i=0; i<events.size(); i++)
    {
        event_t *n = events.at(i);
        if (event_match(criterion, *n))
        {
            killme.push_back(n);

            /*
              If this event was a signal handler and no other handler handles
              the specified signal type, do not handle that type of signal any
              more.
            */
            if (n->type == EVENT_SIGNAL)
            {
                event_t e = event_t::signal_event(n->param1.signal);
                if (event_get(e, 0) == 1)
                {
                    signal_handle(e.param1.signal, 0);
                }
            }
        }
        else
        {
            new_list.push_back(n);
        }
    }
    signal_block();
    events.swap(new_list);
    signal_unblock();
}
Exemple #13
0
void event_add_handler(const event_t *event)
{
    event_t *e;

    CHECK(event,);

    e = event_copy(event, 0);

    if (e->type == EVENT_SIGNAL)
    {
        signal_handle(e->param1.signal, 1);
    }

    // Block around updating the events vector
    signal_block();
    events.push_back(e);
    signal_unblock();
}
Exemple #14
0
/**
   Load contents of the backing file to memory
*/
static void history_load( history_mode_t *m )
{
    int fd;
    int ok=0;

    void *context;
    wchar_t *filename;

    if( !m )
        return;

    m->has_loaded=1;

    signal_block();

    context = halloc( 0, 0 );
    filename = history_filename( context, m->name, 0 );

    if( filename )
    {
        if( ( fd = wopen( filename, O_RDONLY ) ) > 0 )
        {
            off_t len = lseek( fd, 0, SEEK_END );
            if( len != (off_t)-1)
            {
                m->mmap_length = (size_t)len;
                if( lseek( fd, 0, SEEK_SET ) == 0 )
                {
                    if( (m->mmap_start = mmap( 0, m->mmap_length, PROT_READ, MAP_PRIVATE, fd, 0 )) != MAP_FAILED )
                    {
                        ok = 1;
                        history_populate_from_mmap( m );
                    }
                }
            }
            close( fd );
        }
    }

    halloc_free( context );
    signal_unblock();
}
Exemple #15
0
void event_add_handler(const event_t &event)
{
    event_t *e;

    if (debug_level >= 3)
    {
        wcstring desc = event_desc_compact(event);
        debug(3, "register: %ls\n", desc.c_str());
    }

    e = new event_t(event);

    if (e->type == EVENT_SIGNAL)
    {
        signal_handle(e->param1.signal, 1);
    }

    // Block around updating the events vector
    signal_block();
    events.push_back(e);
    signal_unblock();
}
Exemple #16
0
/** Save the specified mode to file */
void history_t::save_internal()
{
    /* This must be called while locked */
    ASSERT_IS_LOCKED(lock);
    
    /* Nothing to do if there's no new items */
    if (new_items.empty() && deleted_items.empty())
        return;
        
    /* Compact our new items so we don't have duplicates */
    this->compact_new_items();
    
	bool ok = true;
    
    wcstring tmp_name = history_filename(name, L".tmp");    
	if( ! tmp_name.empty() )
	{        
        /* Make an LRU cache to save only the last N elements */
        history_lru_cache_t lru(HISTORY_SAVE_MAX);
        
        /* Insert old items in, from old to new. Merge them with our new items, inserting items with earlier timestamps first. */
        std::vector<history_item_t>::const_iterator new_item_iter = new_items.begin();
        
        /* Map in existing items (which may have changed out from underneath us, so don't trust our old mmap'd data) */
        const char *local_mmap_start = NULL;
        size_t local_mmap_size = 0;
        if (map_file(name, &local_mmap_start, &local_mmap_size)) {
            size_t cursor = 0;
            for (;;) {
                size_t offset = offset_of_next_item(local_mmap_start, local_mmap_size, &cursor, 0);
                /* If we get back -1, we're done */
                if (offset == (size_t)(-1))
                    break;

                /* Try decoding an old item */
                const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset);
                if (old_item.empty() || is_deleted(old_item))
                {
//                    debug(0, L"Item is deleted : %s\n", old_item.str().c_str());
                    continue;
                }
                /* The old item may actually be more recent than our new item, if it came from another session. Insert all new items at the given index with an earlier timestamp. */
                for (; new_item_iter != new_items.end(); ++new_item_iter) {
                    if (new_item_iter->timestamp() < old_item.timestamp()) {
                        /* This "new item" is in fact older. */
                        lru.add_item(*new_item_iter);
                    } else {
                        /* The new item is not older. */
                        break;
                    }
                }
                
                /* Now add this old item */
                lru.add_item(old_item);
            }
            munmap((void *)local_mmap_start, local_mmap_size);
        }
        
        /* Insert any remaining new items */
        for (; new_item_iter != new_items.end(); ++new_item_iter)
        {
            lru.add_item(*new_item_iter);
        }
                
        signal_block();
    
        FILE *out;
		if( (out=wfopen( tmp_name, "w" ) ) )
		{
            /* Write them out */
            for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) {
                const history_lru_node_t *node = *iter;
                if (! node->write_yaml_to_file(out)) {
                    ok = false;
                    break;
                }
            }
            
			if( fclose( out ) || !ok )
			{
				/*
				  This message does not have high enough priority to
				  be shown by default.
				*/
				debug( 2, L"Error when writing history file" );
			}
			else
			{
                wcstring new_name = history_filename(name, wcstring());
				wrename(tmp_name, new_name);
			}
		}
        
        signal_unblock();
        
        /* Make sure we clear all nodes, since this doesn't happen automatically */
        lru.evict_all_nodes();
        
        /* We've saved everything, so we have no more unsaved items */
        unsaved_item_count = 0;
	}	

	if( ok )
	{
		/* Our history has been written to the file, so clear our state so we can re-reference the file. */
		this->clear_file_state();
	}
}
Exemple #17
0
int
main (int argc, char *argv[], char *envp[])
{
  int index;
  struct servtab *sep;
  int dofork;
  pid_t pid;

  set_program_name (argv[0]);

  Argv = argv;
  if (envp == 0 || *envp == 0)
    envp = argv;
  while (*envp)
    envp++;
  LastArg = envp[-1] + strlen (envp[-1]);

  /* Parse command line */
  iu_argp_init ("inetd", program_authors);
  argp_parse (&argp, argc, argv, 0, &index, NULL);

  if (resolve_option)
    env_option = true;

  if (index < argc)
    {
      int i;
      config_files = calloc (argc - index + 1, sizeof (*config_files));
      for (i = 0; index < argc; index++, i++)
	{
	  config_files[i] = strdup (argv[index]);
	}
    }
  else
    {
      config_files = calloc (3, sizeof (*config_files));
      config_files[0] = newstr (PATH_INETDCONF);
      config_files[1] = newstr (PATH_INETDDIR);
    }

  if (!debug)
    {
      daemon (0, 0);
    }

  openlog ("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);

  {
    FILE *fp = fopen (PATH_INETDPID, "w");
    if (fp != NULL)
      {
	fprintf (fp, "%d\n", getpid ());
	fclose (fp);
      }
    else
      syslog (LOG_CRIT, "can't open %s: %s\n", PATH_INETDPID,
	      strerror (errno));
  }

  signal_set_handler (SIGALRM, retry);
  config (0);
  signal_set_handler (SIGHUP, config);
  signal_set_handler (SIGCHLD, reapchild);
  signal_set_handler (SIGPIPE, SIG_IGN);

  {
    /* space for daemons to overwrite environment for ps */
#define DUMMYSIZE	100
    char dummy[DUMMYSIZE];

    memset (dummy, 'x', DUMMYSIZE - 1);
    dummy[DUMMYSIZE - 1] = '\0';
    setenv ("inetd_dummy", dummy, 1);
  }

  for (;;)
    {
      int n, ctrl;
      fd_set readable;

      if (nsock == 0)
	{
	  SIGSTATUS stat;
	  sigstatus_empty (stat);

	  signal_block (NULL);
	  while (nsock == 0)
	    inetd_pause (stat);
	  signal_unblock (NULL);
	}
      readable = allsock;
      if ((n = select (maxsock + 1, &readable, NULL, NULL, NULL)) <= 0)
	{
	  if (n < 0 && errno != EINTR)
	    syslog (LOG_WARNING, "select: %m");
	  sleep (1);
	  continue;
	}
      for (sep = servtab; n && sep; sep = sep->se_next)
	if (sep->se_fd != -1 && FD_ISSET (sep->se_fd, &readable))
	  {
	    n--;
	    if (debug)
	      fprintf (stderr, "someone wants %s\n", sep->se_service);
	    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
	      {
		struct sockaddr_in sa_client;
		socklen_t len = sizeof (sa_client);

		ctrl = accept (sep->se_fd, (struct sockaddr *) &sa_client,
			       &len);
		if (debug)
		  fprintf (stderr, "accept, ctrl %d\n", ctrl);
		if (ctrl < 0)
		  {
		    if (errno != EINTR)
		      syslog (LOG_WARNING, "accept (for %s): %m",
			      sep->se_service);
		    continue;
		  }
		if (env_option)
		  prepenv (ctrl, sa_client);
	      }
	    else
	      ctrl = sep->se_fd;

	    signal_block (NULL);
	    pid = 0;
	    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
	    if (dofork)
	      {
		if (sep->se_count++ == 0)
		  gettimeofday (&sep->se_time, NULL);
		else if ((sep->se_max && sep->se_count > sep->se_max)
			 || sep->se_count >= toomany)
		  {
		    struct timeval now;

		    gettimeofday (&now, NULL);
		    if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL)
		      {
			sep->se_time = now;
			sep->se_count = 1;
		      }
		    else
		      {
			syslog (LOG_ERR,
				"%s/%s server failing (looping), service terminated",
				sep->se_service, sep->se_proto);
			close_sep (sep);
			if (! sep->se_wait && sep->se_socktype == SOCK_STREAM)
			  close (ctrl);
			signal_unblock (NULL);
			if (!timingout)
			  {
			    timingout = 1;
			    alarm (RETRYTIME);
			  }
			continue;
		      }
		  }
		pid = fork ();
	      }
	    if (pid < 0)
	      {
		syslog (LOG_ERR, "fork: %m");
		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
		  close (ctrl);
		signal_unblock (NULL);
		sleep (1);
		continue;
	      }
	    if (pid && sep->se_wait)
	      {
		sep->se_wait = pid;
		if (sep->se_fd >= 0)
		  {
		    FD_CLR (sep->se_fd, &allsock);
		    nsock--;
		  }
	      }
	    signal_unblock (NULL);
	    if (pid == 0)
	      {
		if (debug && dofork)
		  setsid ();
		if (dofork)
		  {
		    int sock;
		    if (debug)
		      fprintf (stderr, "+ Closing from %d\n", maxsock);
		    for (sock = maxsock; sock > 2; sock--)
		      if (sock != ctrl)
			close (sock);
		  }
		run_service (ctrl, sep);
	      }
	    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
	      close (ctrl);
	  }
    }
}
Exemple #18
0
struct servtab *
enter (struct servtab *cp)
{
  struct servtab *sep;
  SIGSTATUS sigstatus;
  int i;

  /* Checking/Removing duplicates */
  for (sep = servtab; sep; sep = sep->se_next)
    if (memcmp (&sep->se_ctrladdr, &cp->se_ctrladdr,
		sizeof (sep->se_ctrladdr)) == 0
	&& strcmp (sep->se_service, cp->se_service) == 0
	&& strcmp (sep->se_proto, cp->se_proto) == 0
	&& ISMUX (sep) == ISMUX (cp))
      break;
  if (sep != 0)
    {
      signal_block (&sigstatus);
      /*
       * sep->se_wait may be holding the pid of a daemon
       * that we're waiting for.  If so, don't overwrite
       * it unless the config file explicitly says don't
       * wait.
       */
      if (cp->se_bi == 0 && (sep->se_wait == 1 || cp->se_wait == 0))
	sep->se_wait = cp->se_wait;
#define SWAP(a, b) { char *c = a; a = b; b = c; }
      if (cp->se_user)
	SWAP (sep->se_user, cp->se_user);
      if (cp->se_server)
	SWAP (sep->se_server, cp->se_server);
      argcv_free (sep->se_argc, sep->se_argv);
      sep->se_argc = cp->se_argc;
      sep->se_argv = cp->se_argv;
      cp->se_argc = 0;
      cp->se_argv = NULL;
      sep->se_checked = 1;
      signal_unblock (&sigstatus);
      if (debug)
	print_service ("REDO", sep);
      return sep;
    }

  if (debug)
    print_service ("ADD ", cp);

  sep = (struct servtab *) malloc (sizeof (*sep));
  if (sep == NULL)
    {
      syslog (LOG_ERR, "Out of memory.");
      exit (-1);
    }
  *sep = *cp;
  dupstr (&sep->se_node);
  dupstr (&sep->se_service);
  dupstr (&sep->se_proto);
  dupstr (&sep->se_user);
  dupstr (&sep->se_server);
  dupmem ((void**)&sep->se_argv, sep->se_argc * sizeof (sep->se_argv[0]));
  for (i = 0; i < sep->se_argc; i++)
    dupstr (&sep->se_argv[i]);

  sep->se_fd = -1;
  signal_block (&sigstatus);
  sep->se_next = servtab;
  servtab = sep;
  signal_unblock (&sigstatus);
  return sep;
}
Exemple #19
0
/// Give ownership of the terminal to the specified job.
///
/// \param j The job to give the terminal to.
/// \param cont If this variable is set, we are giving back control to a job that has previously
/// been stopped. In that case, we need to set the terminal attributes to those saved in the job.
bool terminal_give_to_job(job_t *j, int cont) {
    errno = 0;
    if (j->pgid == 0) {
        debug(2, "terminal_give_to_job() returning early due to no process group");
        return true;
    }

    signal_block();

    // It may not be safe to call tcsetpgrp if we've already done so, as at that point we are no
    // longer the controlling process group for the terminal and no longer have permission to set
    // the process group that is in control, causing tcsetpgrp to return EPERM, even though that's
    // not the documented behavior in tcsetpgrp(3), which instead says other bad things will happen
    // (it says SIGTTOU will be sent to all members of the background *calling* process group, but
    // it's more complicated than that, SIGTTOU may or may not be sent depending on the TTY
    // configuration and whether or not signal handlers for SIGTTOU are installed. Read:
    // http://curiousthing.org/sigttin-sigttou-deep-dive-linux In all cases, our goal here was just
    // to hand over control of the terminal to this process group, which is a no-op if it's already
    // been done.
    if (tcgetpgrp(STDIN_FILENO) == j->pgid) {
        debug(4, L"Process group %d already has control of terminal\n", j->pgid);
    } else {
        debug(4,
              L"Attempting to bring process group to foreground via tcsetpgrp for job->pgid %d\n",
              j->pgid);

        // The tcsetpgrp(2) man page says that EPERM is thrown if "pgrp has a supported value, but
        // is not the process group ID of a process in the same session as the calling process."
        // Since we _guarantee_ that this isn't the case (the child calls setpgid before it calls
        // SIGSTOP, and the child was created in the same session as us), it seems that EPERM is
        // being thrown because of an caching issue - the call to tcsetpgrp isn't seeing the
        // newly-created process group just yet. On this developer's test machine (WSL running Linux
        // 4.4.0), EPERM does indeed disappear on retry. The important thing is that we can
        // guarantee the process isn't going to exit while we wait (which would cause us to possibly
        // block indefinitely).
        while (tcsetpgrp(STDIN_FILENO, j->pgid) != 0) {
            bool pgroup_terminated = false;
            if (errno == EINTR) {
                ;  // Always retry on EINTR, see comments in tcsetattr EINTR code below.
            } else if (errno == EINVAL) {
                // OS X returns EINVAL if the process group no longer lives. Probably other OSes,
                // too. Unlike EPERM below, EINVAL can only happen if the process group has
                // terminated.
                pgroup_terminated = true;
            } else if (errno == EPERM) {
                // Retry so long as this isn't because the process group is dead.
                int wait_result = waitpid(-1 * j->pgid, &wait_result, WNOHANG);
                if (wait_result == -1) {
                    // Note that -1 is technically an "error" for waitpid in the sense that an
                    // invalid argument was specified because no such process group exists any
                    // longer. This is the observed behavior on Linux 4.4.0. a "success" result
                    // would mean processes from the group still exist but is still running in some
                    // state or the other.
                    pgroup_terminated = true;
                } else {
                    // Debug the original tcsetpgrp error (not the waitpid errno) to the log, and
                    // then retry until not EPERM or the process group has exited.
                    debug(2, L"terminal_give_to_job(): EPERM.\n", j->pgid);
                }
            } else {
                if (errno == ENOTTY) redirect_tty_output();
                debug(1, _(L"Could not send job %d ('%ls') with pgid %d to foreground"), j->job_id,
                      j->command_wcstr(), j->pgid);
                wperror(L"tcsetpgrp");
                signal_unblock();
                return false;
            }

            if (pgroup_terminated) {
                // All processes in the process group has exited. Since we force all child procs to
                // SIGSTOP on startup, the only way that can happen is if the very last process in
                // the group terminated, and didn't need to access the terminal, otherwise it would
                // have hung waiting for terminal IO (SIGTTIN). We can ignore this.
                debug(3, L"tcsetpgrp called but process group %d has terminated.\n", j->pgid);
                break;
            }
        }
    }

    if (cont) {
        int result = -1;
        // TODO: Remove this EINTR loop since we have blocked all signals and thus cannot be
        // interrupted. I'm leaving it in place because all of the logic involving controlling
        // terminal management is more than a little opaque and smacks of voodoo programming.
        errno = EINTR;
        while (result == -1 && errno == EINTR) {
            result = tcsetattr(STDIN_FILENO, TCSADRAIN, &j->tmodes);
        }
        if (result == -1) {
            if (errno == ENOTTY) redirect_tty_output();
            debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id,
                  j->command_wcstr());
            wperror(L"tcsetattr");
            signal_unblock();
            return false;
        }
    }

    signal_unblock();
    return true;
}
Exemple #20
0
/**
   Save the specified mode to file
*/
static void history_save_mode( void *n, history_mode_t *m )
{
    FILE *out;
    history_mode_t *on_disk;
    int i;
    int has_new=0;
    wchar_t *tmp_name;

    int ok = 1;

    /*
      First check if there are any new entries to save. If not, then
      we can just return
    */
    for( i=0; i<al_get_count(&m->item); i++ )
    {
        void *ptr = al_get( &m->item, i );
        has_new = item_is_new( m, ptr );
        if( has_new )
        {
            break;
        }
    }

    if( !has_new )
    {
        return;
    }

    signal_block();

    /*
      Set up on_disk variable to describe the current contents of the
      history file
    */
    on_disk = history_create_mode( m->name );
    history_load( on_disk );

    tmp_name = history_filename( on_disk, m->name, L".tmp" );

    if( tmp_name )
    {
        tmp_name = wcsdup(tmp_name );

        if( (out=wfopen( tmp_name, "w" ) ) )
        {
            hash_table_t mine;

            hash_init( &mine, &hash_item_func, &hash_item_cmp );

            for( i=0; i<al_get_count(&m->item); i++ )
            {
                void *ptr = al_get( &m->item, i );
                int is_new = item_is_new( m, ptr );
                if( is_new )
                {
                    hash_put( &mine, item_get( m, ptr ), L"" );
                }
            }

            /*
              Re-save the old history
            */
            for( i=0; ok && (i<al_get_count(&on_disk->item)); i++ )
            {
                void *ptr = al_get( &on_disk->item, i );
                item_t *i = item_get( on_disk, ptr );
                if( !hash_get( &mine, i ) )
                {
                    if( item_write( out, on_disk, ptr ) == -1 )
                    {
                        ok = 0;
                        break;
                    }
                }

            }

            hash_destroy( &mine );

            /*
              Add our own items last
            */
            for( i=0; ok && (i<al_get_count(&m->item)); i++ )
            {
                void *ptr = al_get( &m->item, i );
                int is_new = item_is_new( m, ptr );
                if( is_new )
                {
                    if( item_write( out, m, ptr ) == -1 )
                    {
                        ok = 0;
                    }
                }
            }

            if( fclose( out ) || !ok )
            {
                /*
                  This message does not have high enough priority to
                  be shown by default.
                */
                debug( 2, L"Error when writing history file" );
            }
            else
            {
                wrename( tmp_name, history_filename( on_disk, m->name, 0 ) );
            }
        }
        free( tmp_name );
    }

    halloc_free( on_disk);

    if( ok )
    {

        /*
          Reset the history. The item_t entries created in this session
          are not lost or dropped, they are stored in the session_item
          hash table. On reload, they will be automatically inserted at
          the end of the history list.
        */

        if( m->mmap_start && (m->mmap_start != MAP_FAILED ) )
        {
            munmap( m->mmap_start, m->mmap_length );
        }

        al_truncate( &m->item, 0 );
        al_truncate( &m->used, 0 );
        m->pos = 0;
        m->has_loaded = 0;
        m->mmap_start=0;
        m->mmap_length=0;

        m->save_timestamp=time(0);
        m->new_count = 0;
    }

    signal_unblock();
}
static unsigned int data_output_main(int sm_sock_fd, int fix_size)
{
	int sig = 0;
	int r_size;
	int w_size;
	int dev_path = PRNT_PATH;
	unsigned int total = 0; /* total data size (wrote size) */
	char *buf;
	char *ptr;

	/* read buffer get */
	buf = (char *)malloc(MAX_DATA);
	if(!buf)
		exit(0); /* no ram */

	/* print data read(from stdin) and write(to stdout) */
	while((r_size = read_data(buf)) > 0){
		ptr = buf;
		/* data print(output) loop */
		for(;r_size > 0;r_size -= w_size){
			int __attribute__ ((unused)) err;

			signal_block(sigmask);   /* signal block */
			get_printer_sem(sem_id); /* printer lock */

			w_size = p_dvacs->ptdev_write(dev_path, ptr, r_size); /* data write -> printer */
			err = errno;
#ifdef DATA_LOG
                        if(log_hand >=0 && w_size > 0)
                                write(log_hand, ptr, w_size);
#endif

			/* write() error check (power off??) */
			if(w_size <= 0){
				w_size = 0;
				if(check_power(sm_sock_fd, dev_path) == LM_PRN_POWOFF){
#ifdef DEBUG
					write_log("LM(P) into wait_restart()\n");
#endif
					release_printer_sem(sem_id);
					signal_unblock(sigmask);

					/* wait BSCC SUCCESS */
					if(wait_restart() == DETECT_END)
						goto dataout_exit;
					else
						continue;
				}
#ifdef DEBUG
				fprintf(log_path, "LM(P) write error. normal=%x\n",err);
				fflush(log_path);
#endif
				sleep(1);
			}
			else
				total += w_size; /* total write size save */

			/* write() is normal end */
			release_printer_sem(sem_id);
			signal_unblock(sigmask);

			if((sig = check_signals()) != 0){
			/* signal detect */
				if(signal_end(sig)){
					goto dataout_exit; /* SIGTERM */
					}

					/* other signal ignore */
					start_signal = 0;
					last_signal  = 0;
			}
			ptr += w_size;
		}

		/* written data size == request data size ?*/
		if(fix_size && (total >= fix_size) )
			break;
	}
dataout_exit:
	free(buf);

	return total;
}
Exemple #22
0
void exec( job_t *j )
{
	process_t *p;
	pid_t pid;
	int mypipe[2];
	sigset_t chldset; 
	int skip_fork;
	
	io_data_t pipe_read, pipe_write;
	io_data_t *tmp;

	io_data_t *io_buffer =0;

	/*
	  Set to 1 if something goes wrong while exec:ing the job, in
	  which case the cleanup code will kick in.
	*/
	int exec_error=0;

	int needs_keepalive = 0;
	process_t keepalive;
	

	CHECK( j, );
	CHECK_BLOCK();
	
	if( no_exec )
		return;
	
	sigemptyset( &chldset );
	sigaddset( &chldset, SIGCHLD );
	
	debug( 4, L"Exec job '%ls' with id %d", j->command, j->job_id );	
	
	if( block_io )
	{
		if( j->io )
		{
			j->io = io_add( io_duplicate( j, block_io), j->io );
		}
		else
		{
			j->io=io_duplicate( j, block_io);				
		}
	}

	
	io_data_t *input_redirect;

	for( input_redirect = j->io; input_redirect; input_redirect = input_redirect->next )
	{
		if( (input_redirect->io_mode == IO_BUFFER) && 
			input_redirect->is_input )
		{
			/*
			  Input redirection - create a new gobetween process to take
			  care of buffering
			*/
			process_t *fake = halloc( j, sizeof(process_t) );
			fake->type = INTERNAL_BUFFER;
			fake->pipe_write_fd = 1;
			j->first_process->pipe_read_fd = input_redirect->fd;
			fake->next = j->first_process;
			j->first_process = fake;
			break;
		}
	}
	
	if( j->first_process->type==INTERNAL_EXEC )
	{
		/*
		  Do a regular launch -  but without forking first...
		*/
		signal_block();

		/*
		  setup_child_process makes sure signals are properly set
		  up. It will also call signal_unblock
		*/
		if( !setup_child_process( j, 0 ) )
		{
			/*
			  launch_process _never_ returns
			*/
			launch_process( j->first_process );
		}
		else
		{
			job_set_flag( j, JOB_CONSTRUCTED, 1 );
			j->first_process->completed=1;
			return;
		}

	}	

	pipe_read.fd=0;
	pipe_write.fd=1;
	pipe_read.io_mode=IO_PIPE;
	pipe_read.param1.pipe_fd[0] = -1;
	pipe_read.param1.pipe_fd[1] = -1;
	pipe_read.is_input = 1;

	pipe_write.io_mode=IO_PIPE;
	pipe_write.is_input = 0;
	pipe_read.next=0;
	pipe_write.next=0;
	pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1;
	
	j->io = io_add( j->io, &pipe_write );
	
	signal_block();

	/*
	  See if we need to create a group keepalive process. This is
	  a process that we create to make sure that the process group
	  doesn't die accidentally, and is often needed when a
	  builtin/block/function is inside a pipeline, since that
	  usually means we have to wait for one program to exit before
	  continuing in the pipeline, causing the group leader to
	  exit.
	*/
	
	if( job_get_flag( j, JOB_CONTROL ) )
	{
		for( p=j->first_process; p; p = p->next )
		{
			if( p->type != EXTERNAL )
			{
				if( p->next )
				{
					needs_keepalive = 1;
					break;
				}
				if( p != j->first_process )
				{
					needs_keepalive = 1;
					break;
				}
				
			}
			
		}
	}
		
	if( needs_keepalive )
	{
		keepalive.pid = exec_fork();

		if( keepalive.pid == 0 )
		{
			keepalive.pid = getpid();
			set_child_group( j, &keepalive, 1 );
			pause();			
			exit(0);
		}
		else
		{
			set_child_group( j, &keepalive, 0 );			
		}
	}
	
	/*
	  This loop loops over every process_t in the job, starting it as
	  appropriate. This turns out to be rather complex, since a
	  process_t can be one of many rather different things.

	  The loop also has to handle pipelining between the jobs.
	*/

	for( p=j->first_process; p; p = p->next )
	{
		mypipe[1]=-1;
		skip_fork=0;
		
		pipe_write.fd = p->pipe_write_fd;
		pipe_read.fd = p->pipe_read_fd;
//		debug( 0, L"Pipe created from fd %d to fd %d", pipe_write.fd, pipe_read.fd );
		

		/* 
		   This call is used so the global environment variable array
		   is regenerated, if needed, before the fork. That way, we
		   avoid a lot of duplicate work where EVERY child would need
		   to generate it, since that result would not get written
		   back to the parent. This call could be safely removed, but
		   it would result in slightly lower performance - at least on
		   uniprocessor systems.
		*/
		if( p->type == EXTERNAL )
			env_export_arr( 1 );
		
		
		/*
		  Set up fd:s that will be used in the pipe 
		*/
		
		if( p == j->first_process->next )
		{
			j->io = io_add( j->io, &pipe_read );
		}
		
		if( p->next )
		{
//			debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]);
			
			if( exec_pipe( mypipe ) == -1 )
			{
				debug( 1, PIPE_ERROR );
				wperror (L"pipe");
				exec_error=1;
				break;
			}

			memcpy( pipe_write.param1.pipe_fd, mypipe, sizeof(int)*2);
		}
		else
		{
			/*
			  This is the last element of the pipeline.
			  Remove the io redirection for pipe output.
			*/
			j->io = io_remove( j->io, &pipe_write );
			
		}

		switch( p->type )
		{
			case INTERNAL_FUNCTION:
			{
				const wchar_t * orig_def;
				wchar_t * def=0;
				array_list_t *named_arguments;
				int shadows;
				

				/*
				  Calls to function_get_definition might need to
				  source a file as a part of autoloading, hence there
				  must be no blocks.
				*/

				signal_unblock();
				orig_def = function_get_definition( p->argv[0] );
				named_arguments = function_get_named_arguments( p->argv[0] );
				shadows = function_get_shadows( p->argv[0] );

				signal_block();
				
				if( orig_def )
				{
					def = halloc_register( j, wcsdup(orig_def) );
				}
				if( def == 0 )
				{
					debug( 0, _( L"Unknown function '%ls'" ), p->argv[0] );
					break;
				}

				parser_push_block( shadows?FUNCTION_CALL:FUNCTION_CALL_NO_SHADOW );
				
				current_block->param2.function_call_process = p;
				current_block->param1.function_call_name = halloc_register( current_block, wcsdup( p->argv[0] ) );
						

				/*
				  set_argv might trigger an event
				  handler, hence we need to unblock
				  signals.
				*/
				signal_unblock();
				parse_util_set_argv( p->argv+1, named_arguments );
				signal_block();
								
				parser_forbid_function( p->argv[0] );

				if( p->next )
				{
					io_buffer = io_buffer_create( 0 );					
					j->io = io_add( j->io, io_buffer );
				}
				
				internal_exec_helper( def, TOP, j->io );
				
				parser_allow_function();
				parser_pop_block();
				
				break;				
			}
			
			case INTERNAL_BLOCK:
			{
				if( p->next )
				{
					io_buffer = io_buffer_create( 0 );					
					j->io = io_add( j->io, io_buffer );
				}
								
				internal_exec_helper( p->argv[0], TOP, j->io );			
				break;
				
			}

			case INTERNAL_BUILTIN:
			{
				int builtin_stdin=0;
				int fg;
				int close_stdin=0;

				/*
				  If this is the first process, check the io
				  redirections and see where we should be reading
				  from.
				*/
				if( p == j->first_process )
				{
					io_data_t *in = io_get( j->io, 0 );
					
					if( in )
					{
						switch( in->io_mode )
						{
							
							case IO_FD:
							{
								builtin_stdin = in->param1.old_fd;
								break;
							}
							case IO_PIPE:
							{
								builtin_stdin = in->param1.pipe_fd[0];
								break;
							}
							
							case IO_FILE:
							{
								builtin_stdin=wopen( in->param1.filename,
                                              in->param2.flags, OPEN_MASK );
								if( builtin_stdin == -1 )
								{
									debug( 1, 
										   FILE_ERROR,
										   in->param1.filename );
									wperror( L"open" );
								}
								else
								{
									close_stdin = 1;
								}
								
								break;
							}
	
							case IO_CLOSE:
							{
								/*
								  FIXME:

								  When
								  requesting
								  that
								  stdin
								  be
								  closed,
								  we
								  really
								  don't
								  do
								  anything. How
								  should
								  this
								  be
								  handled?
								 */
								builtin_stdin = -1;
								
								break;
							}
							
							default:
							{
								builtin_stdin=-1;
								debug( 1, 
									   _( L"Unknown input redirection type %d" ),
									   in->io_mode);
								break;
							}
						
						}
					}
				}
				else
				{
					builtin_stdin = pipe_read.param1.pipe_fd[0];
				}

				if( builtin_stdin == -1 )
				{
					exec_error=1;
					break;
				}
				else
				{
					int old_out = builtin_out_redirect;
					int old_err = builtin_err_redirect;

					/* 
					   Since this may be the foreground job, and since
					   a builtin may execute another foreground job,
					   we need to pretend to suspend this job while
					   running the builtin, in order to avoid a
					   situation where two jobs are running at once.

					   The reason this is done here, and not by the
					   relevant builtins, is that this way, the
					   builtin does not need to know what job it is
					   part of. It could probably figure that out by
					   walking the job list, but it seems more robust
					   to make exec handle things.
					*/
					
					builtin_push_io( builtin_stdin );
					
					builtin_out_redirect = has_fd( j->io, 1 );
					builtin_err_redirect = has_fd( j->io, 2 );		

					fg = job_get_flag( j, JOB_FOREGROUND );
					job_set_flag( j, JOB_FOREGROUND, 0 );
					
					signal_unblock();
					
					p->status = builtin_run( p->argv, j->io );
					
					builtin_out_redirect=old_out;
					builtin_err_redirect=old_err;
					
					signal_block();
					
					/*
					  Restore the fg flag, which is temporarily set to
					  false during builtin execution so as not to confuse
					  some job-handling builtins.
					*/
					job_set_flag( j, JOB_FOREGROUND, fg );
				}
				
				/*
				  If stdin has been redirected, close the redirection
				  stream.
				*/
				if( close_stdin )
				{
					exec_close( builtin_stdin );
				}				
				break;				
			}
		}
		
		if( exec_error )
		{
			break;
		}
		
		switch( p->type )
		{

			case INTERNAL_BLOCK:
			case INTERNAL_FUNCTION:
			{
				int status = proc_get_last_status();
						
				/*
				  Handle output from a block or function. This usually
				  means do nothing, but in the case of pipes, we have
				  to buffer such io, since otherwise the internal pipe
				  buffer might overflow.
				*/
				if( !io_buffer )
				{
					/*
					  No buffer, so we exit directly. This means we
					  have to manually set the exit status.
					*/
					if( p->next == 0 )
					{
						proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status);
					}
					p->completed = 1;
					break;
				}

				j->io = io_remove( j->io, io_buffer );
				
				io_buffer_read( io_buffer );
				
				if( io_buffer->param2.out_buffer->used != 0 )
				{
					pid = exec_fork();

					if( pid == 0 )
					{
						
						/*
						  This is the child process. Write out the contents of the pipeline.
						*/
						p->pid = getpid();
						setup_child_process( j, p );

						exec_write_and_exit(io_buffer->fd, 
											io_buffer->param2.out_buffer->buff,
											io_buffer->param2.out_buffer->used,
											status);
					}
					else
					{
						/* 
						   This is the parent process. Store away
						   information on the child, and possibly give
						   it control over the terminal.
						*/
						p->pid = pid;						
						set_child_group( j, p, 0 );
												
					}					
					
				}
				else
				{
					if( p->next == 0 )
					{
						proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status);
					}
					p->completed = 1;
				}
				
				io_buffer_destroy( io_buffer );
				
				io_buffer=0;
				break;
				
			}


			case INTERNAL_BUFFER:
			{
		
				pid = exec_fork();
				
				if( pid == 0 )
				{
					/*
					  This is the child process. Write out the
					  contents of the pipeline.
					*/
					p->pid = getpid();
					setup_child_process( j, p );
					
					exec_write_and_exit( 1,
										 input_redirect->param2.out_buffer->buff, 
										 input_redirect->param2.out_buffer->used,
										 0);
				}
				else
				{
					/* 
					   This is the parent process. Store away
					   information on the child, and possibly give
					   it control over the terminal.
					*/
					p->pid = pid;						
					set_child_group( j, p, 0 );	
				}	

				break;				
			}
			
			case INTERNAL_BUILTIN:
			{
				int skip_fork;
				
				/*
				  Handle output from builtin commands. In the general
				  case, this means forking of a worker process, that
				  will write out the contents of the stdout and stderr
				  buffers to the correct file descriptor. Since
				  forking is expensive, fish tries to avoid it wehn
				  possible.
				*/

				/*
				  If a builtin didn't produce any output, and it is
				  not inside a pipeline, there is no need to fork
				*/
				skip_fork =
					( !sb_out->used ) &&
					( !sb_err->used ) &&
					( !p->next );
	
				/*
				  If the output of a builtin is to be sent to an internal
				  buffer, there is no need to fork. This helps out the
				  performance quite a bit in complex completion code.
				*/

				io_data_t *io = io_get( j->io, 1 );
				int buffer_stdout = io && io->io_mode == IO_BUFFER;
				
				if( ( !sb_err->used ) && 
					( !p->next ) &&
					( sb_out->used ) && 
					( buffer_stdout ) )
				{
					char *res = wcs2str( (wchar_t *)sb_out->buff );
					b_append( io->param2.out_buffer, res, strlen( res ) );
					skip_fork = 1;
					free( res );
				}

				for( io = j->io; io; io=io->next )
				{
					if( io->io_mode == IO_FILE && wcscmp(io->param1.filename, L"/dev/null" ))
					{
						skip_fork = 0;
					}
				}
				
				if( skip_fork )
				{
					p->completed=1;
					if( p->next == 0 )
					{
						debug( 3, L"Set status of %ls to %d using short circut", j->command, p->status );
						
						int status = proc_format_status(p->status);
						proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status );
					}
					break;
				}

				/*
				  Ok, unfortunatly, we have to do a real fork. Bummer.
				*/
								
				pid = exec_fork();
				if( pid == 0 )
				{

					/*
					  This is the child process. Setup redirections,
					  print correct output to stdout and stderr, and
					  then exit.
					*/
					p->pid = getpid();
					setup_child_process( j, p );
					do_builtin_io( sb_out->used ? (wchar_t *)sb_out->buff : 0, sb_err->used ? (wchar_t *)sb_err->buff : 0 );
					
					exit( p->status );
						
				}
				else
				{
					/* 
					   This is the parent process. Store away
					   information on the child, and possibly give
					   it control over the terminal.
					*/
					p->pid = pid;
						
					set_child_group( j, p, 0 );
										
				}					
				
				break;
			}
			
			case EXTERNAL:
			{
				pid = exec_fork();
				if( pid == 0 )
				{
					/*
					  This is the child process. 
					*/
					p->pid = getpid();
					setup_child_process( j, p );
					launch_process( p );
					
					/*
					  launch_process _never_ returns...
					*/
				}
				else
				{
					/* 
					   This is the parent process. Store away
					   information on the child, and possibly fice
					   it control over the terminal.
					*/
					p->pid = pid;

					set_child_group( j, p, 0 );
															
				}
				break;
			}
			
		}

		if( p->type == INTERNAL_BUILTIN )
			builtin_pop_io();
				
		/* 
		   Close the pipe the current process uses to read from the
		   previous process_t
		*/
		if( pipe_read.param1.pipe_fd[0] >= 0 )
			exec_close( pipe_read.param1.pipe_fd[0] );
		/* 
		   Set up the pipe the next process uses to read from the
		   current process_t
		*/
		if( p->next )
			pipe_read.param1.pipe_fd[0] = mypipe[0];
		
		/* 
		   If there is a next process in the pipeline, close the
		   output end of the current pipe (the surrent child
		   subprocess already has a copy of the pipe - this makes sure
		   we don't leak file descriptors either in the shell or in
		   the children).
		*/
		if( p->next )
		{
			exec_close(mypipe[1]);
		}		
	}

	/*
	  The keepalive process is no longer needed, so we terminate it
	  with extreme prejudice
	*/
	if( needs_keepalive )
	{
		kill( keepalive.pid, SIGKILL );
	}
	
	signal_unblock();	

	debug( 3, L"Job is constructed" );

	j->io = io_remove( j->io, &pipe_read );

	for( tmp = block_io; tmp; tmp=tmp->next )
		j->io = io_remove( j->io, tmp );
	
	job_set_flag( j, JOB_CONSTRUCTED, 1 );

	if( !job_get_flag( j, JOB_FOREGROUND ) )
	{
		proc_last_bg_pid = j->pgid;
	}

	if( !exec_error )
	{
		job_continue (j, 0);
	}
	
}
Exemple #23
0
void job_continue(job_t *j, bool cont)
{
    /*
      Put job first in the job list
    */
    job_promote(j);
    job_set_flag(j, JOB_NOTIFIED, 0);

    CHECK_BLOCK();

    debug(4,
          L"Continue job %d, gid %d (%ls), %ls, %ls",
          j->job_id,
          j->pgid,
          j->command_wcstr(),
          job_is_completed(j)?L"COMPLETED":L"UNCOMPLETED",
          is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE");

    if (!job_is_completed(j))
    {
        if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND))
        {
            /* Put the job into the foreground. Hack: ensure that stdin is marked as blocking first (#176). */
            make_fd_blocking(STDIN_FILENO);

            signal_block();

            bool ok = terminal_give_to_job(j, cont);

            signal_unblock();

            if (!ok)
                return;
        }

        /*
           Send the job a continue signal, if necessary.
        */
        if (cont)
        {
            process_t *p;

            for (p=j->first_process; p; p=p->next)
                p->stopped=0;

            if (job_get_flag(j, JOB_CONTROL))
            {
                if (killpg(j->pgid, SIGCONT))
                {
                    wperror(L"killpg (SIGCONT)");
                    return;
                }
            }
            else
            {
                for (p=j->first_process; p; p=p->next)
                {
                    if (kill(p->pid, SIGCONT) < 0)
                    {
                        wperror(L"kill (SIGCONT)");
                        return;
                    }
                }
            }
        }

        if (job_get_flag(j, JOB_FOREGROUND))
        {
            /* Look for finished processes first, to avoid select() if it's already done. */
            process_mark_finished_children(false);

            /*
               Wait for job to report.
            */
            while (! reader_exit_forced() && ! job_is_stopped(j) && ! job_is_completed(j))
            {
//					debug( 1, L"select_try()" );
                switch (select_try(j))
                {
                    case 1:
                    {
                        read_try(j);
                        process_mark_finished_children(false);
                        break;
                    }
                    
                    case 0:
                    {
                        /* No FDs are ready. Look for finished processes. */
                        process_mark_finished_children(false);
                        break;
                    }

                    case -1:
                    {
                        /*
                          If there is no funky IO magic, we can use
                          waitpid instead of handling child deaths
                          through signals. This gives a rather large
                          speed boost (A factor 3 startup time
                          improvement on my 300 MHz machine) on
                          short-lived jobs.
                         
                          This will return early if we get a signal,
                          like SIGHUP.
                        */
                        process_mark_finished_children(true);
                        break;
                    }
                }
            }
        }
    }

    if (job_get_flag(j, JOB_FOREGROUND))
    {

        if (job_is_completed(j))
        {

            // It's possible that the job will produce output and exit before we've even read from it.
            // We'll eventually read the output, but it may be after we've executed subsequent calls
            // This is why my prompt colors kept getting screwed up - the builtin echo calls
            // were sometimes having their output combined with the set_color calls in the wrong order!
            read_try(j);

            process_t *p = j->first_process;
            while (p->next)
                p = p->next;

            if (WIFEXITED(p->status) || WIFSIGNALED(p->status))
            {
                /*
                   Mark process status only if we are in the foreground
                   and the last process in a pipe, and it is not a short circuited builtin
                */
                if (p->pid)
                {
                    int status = proc_format_status(p->status);
                    //wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command);
                    proc_set_last_status(job_get_flag(j, JOB_NEGATE)?!status:status);
                }
            }
        }

        /* Put the shell back in the foreground. */
        if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND))
        {
            int ok;

            signal_block();

            ok = terminal_return_from_job(j);

            signal_unblock();

            if (!ok)
                return;

        }
    }

}
Exemple #24
0
void job_continue (job_t *j, int cont)
{
	/*
	  Put job first in the job list
	*/
    job_promote(j);
	job_set_flag( j, JOB_NOTIFIED, 0 );

	CHECK_BLOCK();
	
	debug( 4,
		   L"Continue job %d, gid %d (%ls), %ls, %ls",
		   j->job_id, 
		   j->pgid,
		   j->command_wcstr(), 
		   job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED", 
		   is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" );
	
	if( !job_is_completed( j ) )
	{
		if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
		{							
			/* Put the job into the foreground.  */
			int ok;
			
			signal_block();
			
			ok = terminal_give_to_job( j, cont );
			
			signal_unblock();		

			if( !ok )
				return;
			
		}
		
		/* 
		   Send the job a continue signal, if necessary.  
		*/
		if( cont )
		{
			process_t *p;

			for( p=j->first_process; p; p=p->next )
				p->stopped=0;

			if( job_get_flag( j, JOB_CONTROL ) )
			{
				if( killpg( j->pgid, SIGCONT ) )
				{
					wperror( L"killpg (SIGCONT)" );
					return;
				}
			}
			else
			{
				for( p=j->first_process; p; p=p->next )
				{
					if (kill ( p->pid, SIGCONT) < 0)
					{
						wperror (L"kill (SIGCONT)");
						return;
					}		
				}
			}
		}
	
		if( job_get_flag( j, JOB_FOREGROUND ) )
		{
			int quit = 0;
		
			/* 
			   Wait for job to report. Looks a bit ugly because it has to
			   handle the possibility that a signal is dispatched while
			   running job_is_stopped().
			*/
			while( !quit )
			{
				do
				{
					got_signal = 0;
					quit = job_is_stopped( j ) || job_is_completed( j );
				}
				while (got_signal && !quit);
                
                if (quit) {
                    // It's possible that the job will produce output and exit before we've even read from it.
                    // We'll eventually read the output, but it may be after we've executed subsequent calls
                    // This is why my prompt colors kept getting screwed up - the builtin echo calls
                    // were sometimes having their output combined with the set_color calls in the wrong order!
                    read_try(j);
                }

				if( !quit )
				{
					
//					debug( 1, L"select_try()" );	
					switch( select_try(j) )
					{
						case 1:			
						{
							read_try( j );
							break;
						}
					
						case -1:
						{
							/*
							  If there is no funky IO magic, we can use
							  waitpid instead of handling child deaths
							  through signals. This gives a rather large
							  speed boost (A factor 3 startup time
							  improvement on my 300 MHz machine) on
							  short-lived jobs.
							*/
							int status;						
							pid_t pid = waitpid(-1, &status, WUNTRACED );
							if( pid > 0 )
							{
								handle_child_status( pid, status );
							}
							else
							{
								/*
								  This probably means we got a
								  signal. A signal might mean that the
								  terminal emulator sent us a hup
								  signal to tell is to close. If so,
								  we should exit.
								*/
								if( reader_exit_forced() )
								{
									quit = 1;
								}
								
							}
							break;
						}
								
					}
				}					
			}
		}	
	}
	
	if( job_get_flag( j, JOB_FOREGROUND ) )
	{
		
		if( job_is_completed( j ))
		{
			process_t *p = j->first_process;
			while( p->next )
				p = p->next;

			if( WIFEXITED( p->status ) || WIFSIGNALED(p->status))
			{
				/* 
				   Mark process status only if we are in the foreground
				   and the last process in a pipe, and it is not a short circuted builtin
				*/
				if( p->pid )
				{
					int status = proc_format_status(p->status);
					//wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command);
					proc_set_last_status( job_get_flag( j, JOB_NEGATE )?!status:status);
				}
			}			
		}
		/* 
		   Put the shell back in the foreground.  
		*/
		if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
		{
			int ok;
			
			signal_block();

			ok = terminal_return_from_job( j );
			
			signal_unblock();
			
			if( !ok )
				return;
			
		}
	}
	
}
Exemple #25
0
int
init_term(void)
{
#ifndef TGETENT_ACCEPTS_NULL
    static char termbuf[2048];	/* the termcap buffer */
#endif

    if (!*term)
	return termok = TERM_BAD;

    /* unset zle if using zsh under emacs */
    if (!strcmp(term, "emacs"))
	opts[USEZLE] = 0;

#ifdef TGETENT_ACCEPTS_NULL
    /* If possible, we let tgetent allocate its own termcap buffer */
    if (tgetent(NULL, term) != 1) {
#else
    if (tgetent(termbuf, term) != 1) {
#endif

	if (isset(INTERACTIVE))
	    zerr("can't find termcap info for %s", term, 0);
	errflag = 0;
	return termok = TERM_BAD;
    } else {
	char tbuf[1024], *pp;
	int t0;

	termok = TERM_OK;
	for (t0 = 0; t0 != TC_COUNT; t0++) {
	    pp = tbuf;
	    zsfree(tcstr[t0]);
	/* AIX tgetstr() ignores second argument */
	    if (!(pp = tgetstr(tccapnams[t0], &pp)))
		tcstr[t0] = NULL, tclen[t0] = 0;
	    else {
		tclen[t0] = strlen(pp);
		tcstr[t0] = (char *) zalloc(tclen[t0] + 1);
		memcpy(tcstr[t0], pp, tclen[t0] + 1);
	    }
	}

	/* check whether terminal has automargin (wraparound) capability */
	hasam = tgetflag("am");

	/* if there's no termcap entry for cursor up, use single line mode: *
	 * this is flagged by termok which is examined in zle_refresh.c     *
	 */
	if (!tccan(TCUP)) {
		tcstr[TCUP] = NULL;
		termok = TERM_NOUP;
	}

	/* if there's no termcap entry for cursor left, use \b. */
	if (!tccan(TCLEFT)) {
	    tcstr[TCLEFT] = ztrdup("\b");
	    tclen[TCLEFT] = 1;
	}

	/* if the termcap entry for down is \n, don't use it. */
	if (tccan(TCDOWN) && tcstr[TCDOWN][0] == '\n') {
	    tclen[TCDOWN] = 0;
	    zsfree(tcstr[TCDOWN]);
	    tcstr[TCDOWN] = NULL;
	}

	/* if there's no termcap entry for clear, use ^L. */
	if (!tccan(TCCLEARSCREEN)) {
	    tcstr[TCCLEARSCREEN] = ztrdup("\14");
	    tclen[TCCLEARSCREEN] = 1;
	}
    }
    return termok;
}

/* Initialize lots of global variables and hash tables */

/**/
void
setupvals(void)
{
    struct passwd *pswd;
    struct timezone dummy_tz;
    char *ptr;
#ifdef HAVE_GETRLIMIT
    int i;
#endif

    noeval = 0;
    curhist = 0;
    histsiz = DEFAULT_HISTSIZE;
    inithist();
    clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));

    cmdstack = (unsigned char *) zalloc(256);
    cmdsp = 0;

    bangchar = '!';
    hashchar = '#';
    hatchar = '^';
    termok = TERM_BAD;
    curjob = prevjob = coprocin = coprocout = -1;
    gettimeofday(&shtimer, &dummy_tz);	/* init $SECONDS */
    srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */

    hostnam     = (char *) zalloc(256);
    gethostname(hostnam, 256);

    /* Set default path */
    path    = (char **) zalloc(sizeof(*path) * 5);
    path[0] = ztrdup("/bin");
    path[1] = ztrdup("/usr/bin");
    path[2] = ztrdup("/usr/ucb");
    path[3] = ztrdup("/usr/local/bin");
    path[4] = NULL;

    cdpath   = mkarray(NULL);
    manpath  = mkarray(NULL);
    fignore  = mkarray(NULL);
    fpath    = mkarray(NULL);
    mailpath = mkarray(NULL);
    watch    = mkarray(NULL);
    psvar    = mkarray(NULL);
#ifdef DYNAMIC
    module_path = mkarray(ztrdup(MODULE_DIR));
    modules = newlinklist();
#endif

    /* Set default prompts */
    if (opts[INTERACTIVE]) {
	prompt  = ztrdup("%m%# ");
	prompt2 = ztrdup("%_> ");
    } else {
	prompt = ztrdup("");
	prompt2 = ztrdup("");
    }
    prompt3 = ztrdup("?# ");
    prompt4 = ztrdup("+ ");
    sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? ");

    ifs         = ztrdup(DEFAULT_IFS);
    wordchars   = ztrdup(DEFAULT_WORDCHARS);
    postedit    = ztrdup("");
    underscore  = ztrdup("");

    zoptarg = ztrdup("");
    zoptind = 1;
    schedcmds = NULL;

    ppid  = (long) getppid();
    mypid = (long) getpid();
    term  = ztrdup("");

#ifdef TIOCGWINSZ
    if (!(columns = shttyinfo.winsize.ws_col))
	columns = 80;
    if (columns < 2)
	opts[USEZLE] = 0;
    if (!(lines = shttyinfo.winsize.ws_row))
	lines = 24;
    if (lines < 2)
	opts[SINGLELINEZLE] = 1;
#else
    columns = 80;
    lines = 24;
#endif

    /* The following variable assignments cause zsh to behave more *
     * like Bourne and Korn shells when invoked as "sh" or "ksh".  *
     * NULLCMD=":" and READNULLCMD=":"                             */

    if (emulation == EMULATE_KSH || emulation == EMULATE_SH) {
	nullcmd     = ztrdup(":");
	readnullcmd = ztrdup(":");
    } else {
	nullcmd     = ztrdup("cat");
	readnullcmd = ztrdup("more");
    }

    /* We cache the uid so we know when to *
     * recheck the info for `USERNAME'     */
    cached_uid = getuid();

    /* Get password entry and set info for `HOME' and `USERNAME' */
    if ((pswd = getpwuid(cached_uid))) {
	home = metafy(pswd->pw_dir, -1, META_DUP);
	cached_username = ztrdup(pswd->pw_name);
    } else {
	home = ztrdup("/");
	cached_username = ztrdup("");
    }

    /* Try a cheap test to see if we can *
     * initialize `PWD' from `HOME'      */
    if (ispwd(home))
	pwd = ztrdup(home);
    else if ((ptr = zgetenv("PWD")) && ispwd(ptr))
	pwd = ztrdup(ptr);
    else
	pwd = metafy(zgetcwd(), -1, META_REALLOC);

    oldpwd = ztrdup(pwd);  /* initialize `OLDPWD' = `PWD' */
#ifdef __EMX__
    *cdrive = _getdrive();
    strcat(cdrive+1,":");
#endif

    inittyptab();     /* initialize the ztypes table */
    initlextabs();    /* initialize lexing tables    */

    createreswdtable();     /* create hash table for reserved words    */
    createaliastable();     /* create hash table for aliases           */
    createcmdnamtable();    /* create hash table for external commands */
    createshfunctable();    /* create hash table for shell functions   */
    createbuiltintable();   /* create hash table for builtin commands  */
    createnameddirtable();  /* create hash table for named directories */
    createparamtable();     /* create paramater hash table             */

#ifdef ZLE_MODULE
    add_dep("compctl", "zle");
    addbuiltin("bindkey", 0, NULL, 0, -1, "zle");
    addbuiltin("vared", 0, NULL, 1, 7, "zle");
    addbuiltin("compctl", 0, NULL, 0, -1, "compctl");
#endif

#ifdef HAVE_GETRLIMIT
    for (i = 0; i != RLIM_NLIMITS; i++) {
	getrlimit(i, current_limits + i);
	limits[i] = current_limits[i];
    }
#endif

    breaks = loops = 0;
    lastmailcheck = time(NULL);
    locallist = NULL;
    locallevel = sourcelevel = 0;
    trapreturn = 0;
    noerrexit = 0;
    nohistsave = 1;
    dirstack = newlinklist();
    bufstack = newlinklist();
    hsubl = hsubr = NULL;
    lastpid = 0;
    bshin = SHIN ? fdopen(SHIN, "r") : stdin;
    if (isset(SHINSTDIN) && !SHIN && unset(INTERACTIVE)) {
#ifdef _IONBF
	setvbuf(stdin, NULL, _IONBF, 0);
#else
	setlinebuf(stdin);
#endif
    }

    times(&shtms);
}

/* Initialize signal handling */

/**/
void
init_signals(void)
{
    intr();

#ifndef QDEBUG
    signal_ignore(SIGQUIT);
#endif

    install_handler(SIGHUP);
    install_handler(SIGCHLD);
    if (interact) {
	install_handler(SIGALRM);
#ifdef SIGWINCH
	install_handler(SIGWINCH);
#endif
	signal_ignore(SIGTERM);
    }
    if (jobbing) {
	long ttypgrp;

#ifndef __EMX__
	while ((ttypgrp = gettygrp()) != -1 && ttypgrp != mypgrp)
	    kill(0, SIGTTIN);
#endif
	if (ttypgrp == -1) {
	    opts[MONITOR] = 0;
	} else {
#ifndef __EMX__
	    signal_ignore(SIGTTOU);
	    signal_ignore(SIGTSTP);
	    signal_ignore(SIGTTIN);
#endif
	    signal_ignore(SIGPIPE);
	    attachtty(mypgrp);
	}
    }
    if (islogin) {
	signal_setmask(signal_mask(0));
    } else if (interact) {
	sigset_t set;

	sigemptyset(&set);
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGQUIT);
	signal_unblock(set);
    }
}