Esempio n. 1
0
static void client_old_parse_messages(GebrCommProtocolSocket * socket, struct client *client)
{
	GList *link;
	struct gebr_comm_message *message;

	while ((link = g_list_last(client->socket->protocol->messages)) != NULL) {
		message = (struct gebr_comm_message *)link->data;

		/* check login */
		if (message->hash == gebr_comm_protocol_defs.ini_def.code_hash) {
			GList *arguments;

			GString *accounts_list = g_string_new("");
			GString *queue_list = g_string_new("");
			GString *display_port = g_string_new("");

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 2)) == NULL)
				goto err;

			GString *version = g_list_nth_data(arguments, 0);
			GString *hostname = g_list_nth_data(arguments, 1);

			g_debug("Current protocol version is: %s", gebr_comm_protocol_get_version());
			g_debug("Received protocol version:   %s", version->str);

			if (strcmp(version->str, gebr_comm_protocol_get_version())) {
				gebr_comm_protocol_socket_oldmsg_send(client->socket, TRUE,
								      gebr_comm_protocol_defs.err_def, 2,
								      "protocol",
								      gebr_comm_protocol_get_version());
				goto err;
			}

			/* set client info */
			client->socket->protocol->logged = TRUE;
			g_string_assign(client->socket->protocol->hostname, hostname->str);
			client->server_location = GEBR_COMM_SERVER_LOCATION_REMOTE;

			const gchar *server_type;
			if (gebrd_get_server_type() == GEBR_COMM_SERVER_TYPE_MOAB) {
				/* Get info from the MOAB cluster */
				server_moab_read_credentials(accounts_list, queue_list);
				server_type = "moab";
			} else
				server_type = "regular";

			GString *mpi_flavors = g_string_new("");
			g_list_foreach(gebrd->mpi_flavors, (GFunc) get_mpi_flavors, mpi_flavors);
			if (mpi_flavors->len > 0)
				mpi_flavors = g_string_erase(mpi_flavors, mpi_flavors->len-1, 1);

			g_debug("------------on daemon, Sending %s", mpi_flavors->str)  ;
			const gchar *model_name;
			const gchar *cpu_clock;
			const gchar *total_memory;
			GebrdCpuInfo *cpuinfo = gebrd_cpu_info_new();
			GebrdMemInfo *meminfo = gebrd_mem_info_new();
			model_name = gebrd_cpu_info_get (cpuinfo, 0, "model name");
			cpu_clock = gebrd_cpu_info_get (cpuinfo, 0, "cpu MHz");
			total_memory = gebrd_mem_info_get (meminfo, "MemTotal");
			gchar *ncores = g_strdup_printf("%d", gebrd_cpu_info_n_procs(cpuinfo));

			gebr_comm_protocol_socket_oldmsg_send(client->socket, FALSE,
							      gebr_comm_protocol_defs.ret_def, 11,
							      gebrd->hostname,
							      server_type,
							      accounts_list->str,
							      model_name,
							      total_memory,
							      gebrd->fs_lock->str,
							      ncores,
							      cpu_clock,
							      gebrd_user_get_daemon_id(gebrd->user),
							      g_get_home_dir(),
							      mpi_flavors->str);
			gebrd_cpu_info_free(cpuinfo);
			gebrd_mem_info_free(meminfo);
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
			g_string_free(accounts_list, TRUE);
			g_string_free(queue_list, TRUE);
			g_string_free(display_port, TRUE);
			g_free(ncores);
		}
		else if (message->hash == gebr_comm_protocol_defs.gid_def.code_hash) {
			GList *arguments;

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 3)) == NULL)
				goto err;

			GString *gid = g_list_nth_data(arguments, 0);
			GString *cookie = g_list_nth_data(arguments, 1);
			GString *disp_str = g_list_nth_data(arguments, 2);

			guint display;
			if (g_strcmp0(disp_str->str, "0") == 0)
				display = gebr_comm_get_available_port(6010);
			else
				display = atoi(disp_str->str);
			display = MAX(display-6000, 0);

			g_hash_table_insert(gebrd->display_ports, g_strdup(gid->str), GUINT_TO_POINTER(display));

			g_debug("Received gid %s with cookie %s", gid->str, cookie->str);

			if (cookie->len && gebrd_get_server_type() != GEBR_COMM_SERVER_TYPE_MOAB) {
				gebrd_message(GEBR_LOG_DEBUG, "Authorizing with system(xauth)");
				gchar *tmp = g_strdup_printf(":%d", display);
				if (!run_xauth_command("add", tmp, cookie->str))
					display = 0;
				g_free(tmp);
			}

			gchar *display_str = g_strdup_printf("%d", display ? display + 6000 : 0);
			gebrd_message(GEBR_LOG_INFO, "Sending port %s to client %s!", display_str, gid->str);
			gebr_comm_protocol_socket_oldmsg_send(client->socket, FALSE,
							      gebr_comm_protocol_defs.ret_def, 2,
							      gid->str, display_str);
			g_free(display_str);

			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
		}
		else if (client->socket->protocol->logged == FALSE) {
			/* not logged! */
			goto err;
		} else if (message->hash == gebr_comm_protocol_defs.qut_def.code_hash) {
			client_free(client);
			gebr_comm_message_free(message);
			return;
		} else if (message->hash == gebr_comm_protocol_defs.lst_def.code_hash) {
			job_list(client);
		} else if (message->hash == gebr_comm_protocol_defs.run_def.code_hash) {
			GList *arguments;
			GebrdJob *job;

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 9)) == NULL)
				goto err;

			GString *gid = g_list_nth_data(arguments, 0);
			GString *id = g_list_nth_data(arguments, 1);
			GString *frac = g_list_nth_data(arguments, 2);
			GString *numproc = g_list_nth_data(arguments, 3);
			GString *nice = g_list_nth_data(arguments, 4);
			GString *flow_xml = g_list_nth_data(arguments, 5);
			GString *paths = g_list_nth_data(arguments, 6);

			/* Moab & MPI settings */
			GString *account = g_list_nth_data(arguments, 7);
			GString *servers_mpi = g_list_nth_data(arguments, 8);

			g_debug("SERVERS MPI %s", servers_mpi->str);

			/* try to run and send return */
			job_new(&job, client, gid, id, frac, numproc, nice, flow_xml, account, paths, servers_mpi);

#ifdef DEBUG
			gchar *env_delay = getenv("GEBRD_RUN_DELAY_SEC");
			if (env_delay != NULL)
				sleep(atoi(env_delay));
#endif

			if (gebrd_get_server_type() == GEBR_COMM_SERVER_TYPE_REGULAR) {
				/* send job message (job is created -promoted from waiting server response- at the client) */
				g_debug("RUN_DEF: run task with rid %s", id->str);
				job_send_clients_job_notify(job);
				job_run_flow(job);
			} else {
				/* ask moab to run */
				job_run_flow(job);
				/* send job message (job is created -promoted from waiting server response- at the client)
				 * at moab we must run the process before sending the JOB message, because at
				 * job_run_flow moab_jid is acquired.
				 */
				job_send_clients_job_notify(job);
			}

			/* frees */
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
		} else if (message->hash == gebr_comm_protocol_defs.rnq_def.code_hash) {
		} else if (message->hash == gebr_comm_protocol_defs.flw_def.code_hash) {
		} else if (message->hash == gebr_comm_protocol_defs.clr_def.code_hash) {
			GList *arguments;
			GString *rid;
			GebrdJob *job;

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 1)) == NULL)
				goto err;
			rid = g_list_nth_data(arguments, 0);

			job = job_find(rid);
			if (job != NULL)
				job_clear(job);

			/* frees */
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
		} else if (message->hash == gebr_comm_protocol_defs.end_def.code_hash) {
			GList *arguments;
			GString *rid;
			GebrdJob *job;

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 1)) == NULL)
				goto err;
			rid = g_list_nth_data(arguments, 0);

			/* try to run and send return */
			job = job_find(rid);
			if (job != NULL) {
				job_end(job);
			}

			/* frees */
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
		} else if (message->hash == gebr_comm_protocol_defs.kil_def.code_hash) {
			GList *arguments;
			GString *rid;
			GebrdJob *job;

			/* organize message data */
			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 1)) == NULL)
				goto err;
			rid = g_list_nth_data(arguments, 0);

			/* try to run and send return */
			job = job_find(rid);
			if (job != NULL) {
				job_kill(job);
			}

			/* frees */
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);
		} else if (message->hash == gebr_comm_protocol_defs.path_def.code_hash) {
			GList *arguments;

			if ((arguments = gebr_comm_protocol_socket_oldmsg_split(message->argument, 3)) == NULL)
				goto err;

			GString *new_path = g_list_nth_data(arguments, 0);
			GString *old_path = g_list_nth_data(arguments, 1);
			GString *opt = g_list_nth_data(arguments, 2);

			g_debug("new_path:%s, old_path:%s, opt:%s", new_path->str, old_path->str, opt->str);

			GList *new_paths= parse_comma_separated_string(new_path->str);

			gint option = gebr_comm_protocol_path_str_to_enum(opt->str);
			gint status_id = -1;
			gboolean flag_exists = FALSE;
			gboolean flag_error = FALSE;


			switch (option) {
			case GEBR_COMM_PROTOCOL_PATH_CREATE:
				for (GList *j = new_paths; j; j = j->next) {
					GString *path = j->data;
					if (g_file_test(path->str, G_FILE_TEST_IS_DIR)){
						flag_exists = TRUE;
					}
					else if (*(path->str) && g_mkdir_with_parents(path->str, 0700)) {
						flag_error = TRUE;
						break;
					}
					if (g_access(path->str, W_OK)==-1){
						flag_error = TRUE;
						break;
					}
				}

				if (flag_error)
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_ERROR;
				else if (flag_exists)
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_EXISTS;
				else
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_OK;
				break;
			case GEBR_COMM_PROTOCOL_PATH_RENAME:
				g_debug("Renaming %s to %s", old_path->str, new_path->str);
				gboolean dir_exist = g_file_test(new_path->str, G_FILE_TEST_IS_DIR);
				gboolean create_dir = g_rename(old_path->str, new_path->str) == 0;

				if (!dir_exist && create_dir)
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_OK;
				else if (dir_exist && !create_dir)
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_EXISTS;
				else if (!dir_exist && !create_dir)
					status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_ERROR;
				else
					status_id = -1;
				break;
			case GEBR_COMM_PROTOCOL_PATH_DELETE:
				for (GList *j = new_paths; j; j = j->next) {
					GString *path = j->data;
					if (g_rmdir(path->str))
						status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_ERROR;
					else
						status_id = GEBR_COMM_PROTOCOL_STATUS_PATH_OK;
				}
				break;
			default:
				g_warn_if_reached();
				break;
			}
			g_debug("on %s, new_path:'%s', old_path:'%s', status_id: '%d'", __func__, new_path->str, old_path->str, status_id);

			/* frees */
			gebr_comm_protocol_socket_oldmsg_split_free(arguments);

			gebr_comm_protocol_socket_oldmsg_send(socket, FALSE,
							      gebr_comm_protocol_defs.ret_def, 2,
							      gebrd->hostname,
							      g_strdup_printf("%d", status_id));
		} else if (message->hash == gebr_comm_protocol_defs.harakiri_def.code_hash) {
			/* Maestro wants me killed */
			g_debug("Harakiri");
			gebrd_quit();
		} else {
			/* unknown message! */
			goto err;
		}
		gebr_comm_message_free(message);
		client->socket->protocol->messages = g_list_delete_link(client->socket->protocol->messages, link);
	}

	return;

err:	gebr_comm_message_free(message);
	client->socket->protocol->messages = g_list_delete_link(client->socket->protocol->messages, link);
	client_disconnected(socket, client);
}
Esempio n. 2
0
void job_fini()
{
     job_clear();
     if (job_tab)
          itree_destroy(job_tab);
}
Esempio n. 3
0
int main(int argc, char **argv) {
     time_t now;

     /* Initialise route, runq and job classes */
     now = time(NULL);
     route_init(NULL, 0);
     route_register(&rt_filea_method);
     route_register(&rt_fileov_method);
     route_register(&rt_stdin_method);
     route_register(&rt_stdout_method);
     route_register(&rt_stderr_method);
     if ( ! elog_init(1, "job test", NULL))
	  elog_die(FATAL, "didn't initialise elog\n");
     sig_init();
     callback_init();
     runq_init(now);
     meth_init();
     job_init();

     /* Test should fail due to incorrect method */
     elog_printf(DEBUG, "Expect a complaint! -> ");
     if (job_add(5, 5, 0, 1, "test1a1", "internal_test", "stdout", 
		  "stderr", 100, NULL, "echo \"Hello, world\"") != -1)
     {
	  elog_die(FATAL, "[1a] Shouldn't be able to add\n");
     }

     /* Single test in five seconds, never to run */
     if (job_add(5, 5, 0, 1, "test1a2", "internal_test", "stdout", 
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1a] Can't add\n");
     }
				/* Attention: some white box testing */
     itree_first(runq_event);
     if (itree_getkey(runq_event) != now+5) {
	  elog_die(FATAL, "[1a] Queued at an incorrect time\n");
     }
     job_clear();
     if (!itree_empty(runq_event) || !itree_empty(runq_tab)) {
	  elog_die(FATAL, 
		  "[1a] Trees not emptied. runq_events=%d, runq_tab=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab));
     }
     now = time(NULL);
     
     /* Two tests both in five seconds, never to run */
     if (job_add(5, 5, 0, 1, "test1b1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1b] Can't add first\n");
     }
     if (job_add(5, 5, 0, 1, "test1b2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1b] Can't add second\n");
     }
     itree_first(runq_event);
     if (itree_getkey(runq_event) != now+5) {
	  elog_die(FATAL, "[1b] First queued at an incorrect time\n");
     }
     itree_next(runq_event);
     if (itree_getkey(runq_event) != now+5) {
	  elog_die(FATAL, "[1b] Second queued at an incorrect time\n");
     }
     job_clear();
     if (!itree_empty(runq_event) || !itree_empty(runq_tab)) {
	  elog_die(FATAL, 
		  "[1b] Trees not emptied. runq_events=%d, runq_tab=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab));
     }
     now = time(NULL);

     /* Two tests one in five seconds, the other in six, never to run */
     if (job_add(6, 6, 0, 1, "test1c1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1c] Can't add first\n");
     }
     if (job_add(now+5, 5, 0, 1, "test1c2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1c] Can't add second\n");
     }
     itree_first(runq_event);
     if (itree_getkey(runq_event) != now+5) {
	  elog_die(FATAL, "[1c] First queued at an incorrect time\n");
     }
     itree_next(runq_event);
     if (itree_getkey(runq_event) != now+6) {
	  elog_die(FATAL, "[1c] Second queued at an incorrect time\n");
     }
     job_clear();
     if (!itree_empty(runq_event) || !itree_empty(runq_tab)) {
	  elog_die(FATAL, 
		  "[1c] Trees not emptied. runq_events=%d, runq_tab=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab));
     }
     now = time(NULL);
     
     /* Continuous single test supposed to start two seconds ago, 
      * next run in three; never to run */
     if (job_add(-2, 5, 0, 0, "test1d1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1d] Can't add\n");
     }
     itree_first(runq_event);
     if (itree_getkey(runq_event) != now+3) {
	  elog_die(FATAL, 
		  "[1d] Event queued at an incorrect time: bad=%d good=%ld\n", 
		  itree_getkey(runq_event), now+3);
     }
     job_clear();
     if (runq_nsched() > 0) {
	  elog_die(FATAL, "[1d] Still active work scheduled. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }
     now = time(NULL);
     
     /* Two continous tests, starting two seconds ago, next next run in four;
      * never to run */
     if (job_add(-2, 6, 0, 0, "test1e1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1e] Can't add first\n");
     }
     if (job_add(-3, 5, 0, 0, "test1e2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1e] Can't add second\n");
     }
     itree_first(runq_event);
     while (((struct runq_work*) itree_get(runq_event))->expired)
	  itree_next(runq_event);
     if (itree_getkey(runq_event) != now+2) {
	  elog_die(FATAL, "[1e] First queued at an incorrect time\n");
     }
     itree_next(runq_event);
     while (((struct runq_work*) itree_get(runq_event))->expired)
	  itree_next(runq_event);
     if (itree_getkey(runq_event) != now+4) {
	  elog_die(FATAL, "[1e] Second queued at an incorrect time\n");
     }
     job_clear();
     if (runq_nsched() > 0) {
	  elog_die(FATAL, "[1e] Still active work scheduled. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }
     now = time(NULL);
     
     /* Two 5 run jobs, scheduled to start 10 seconds ago, with periods
      * of 5 and 6 seconds; never to run */
     if (job_add(-10, 6, 0, 5, "test1f1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1f] Can't add first\n");
     }
     if (job_add(-10, 5, 0, 5, "test1f2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1f] Can't add second\n");
     }

     itree_first(runq_event);
     while (((struct runq_work*) itree_get(runq_event))->expired)
	  itree_next(runq_event);
     if (itree_getkey(runq_event) != now+2) {
	  elog_die(FATAL, "[1f] First queued at an incorrect time\n");
     }
     itree_next(runq_event);
     while (((struct runq_work*) itree_get(runq_event))->expired)
	  itree_next(runq_event);
     if (itree_getkey(runq_event) != now+5) {
	  elog_die(FATAL, "[1f] Second queued at an incorrect time\n");
     }
     job_clear();
     if (runq_nsched() > 0) {
	  elog_die(FATAL, "[1f] Still active work scheduled. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }
     now = time(NULL);
     
     /* Two 5 run jobs, scheduled to start 100 seconds ago, with periods
      * of 5 and 6 seconds; they should never be scheduled */
     if (job_add(-100, 6, 0, 5, "test1g1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1g] Can't add first\n");
     }
     if (job_add(-100, 5, 0, 5, "test1g2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1g] Can't add second\n");
     }
     if (runq_nsched() > 0) {
	  elog_die(FATAL, "[1g] Still active work scheduled. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }
     job_clear();
     now = time(NULL);

     /* Two five run tests, starting at different times in the past,
      * five runs each wittth different periods; they should both
      * run now */
     if (job_add(-24, 6, 0, 5, "test1h1", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1h] Can't add first\n");
     }
     if (job_add(-20, 5, 0, 5, "test1h2", "internal_test", "stdout",
		  "stderr", 100, "exec", "echo \"Hello, world\"") == -1)
     {
	  elog_die(FATAL, "[1h] Can't add second\n");
     }
     if (runq_nsched() != 2) {
	  elog_die(FATAL, "[1h] Two jobs should be scheduled not %d\n",
		  runq_nsched());
	  runq_dump();
     }
     sig_on();
     sleep(6);		/* let it run */
     sleep(1);		/* let it run */
     sleep(1);		/* let it run */
     sleep(1);		/* let it run */
     sig_off();
     if (runq_nsched() > 0) {
	  elog_die(FATAL, "[1h] Still active work scheduled. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }

     job_clear();

#if 0
     /* check all tables/lists are empty */
     if (!itree_empty(runq_event) || !itree_empty(runq_tab)) {
	  elog_die(FATAL, "[1i] Still entries in tables. runq_events=%d, "
		  "runq_tab=%d runq_nsched()=%d\n", 
		  itree_n(runq_event), itree_n(runq_tab), runq_nsched());
	  runq_dump();
     }
#endif

     job_fini();
     meth_fini();
     runq_fini();
     elog_fini();
     route_fini();
     callback_fini();

     printf("%s: tests finished\n", argv[0]);
     exit(0);
}
Esempio n. 4
0
/*
 * used with command -x to run the command in multiple passes
 * spawn is non-zero when invoked via spawn
 * the exitval is set to the maximum for each execution
 */
static pid_t path_xargs(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn)
{
	register char *cp, **av, **xv;
	char **avlast= &argv[shp->xargmax], **saveargs=0;
	char *const *ev;
	long size, left;
	int nlast=1,n,exitval=0;
	pid_t pid;
	if(shp->xargmin < 0)
		return((pid_t)-1);
	size = shp->gd->lim.arg_max-1024;
	for(ev=envp; cp= *ev; ev++)
		size -= strlen(cp)-1;
	for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++)  
		size -= strlen(cp)-1;
	for(av=avlast; cp= *av; av++,nlast++)  
		size -= strlen(cp)-1;
	av =  &argv[shp->xargmin];
	if(!spawn)
		job_clear();
	shp->exitval = 0;
	while(av<avlast)
	{
		for(xv=av,left=size; left>0 && av<avlast;)
			left -= strlen(*av++)+1;
		/* leave at least two for last */
		if(left<0 && (avlast-av)<2)
			av--;
		if(xv==&argv[shp->xargmin])
		{
			n = nlast*sizeof(char*);
			saveargs = (char**)malloc(n);
			memcpy((void*)saveargs, (void*)av, n);
			memcpy((void*)av,(void*)avlast,n);
		}
		else
		{
			for(n=shp->xargmin; xv < av; xv++)
				argv[n++] = *xv;
			for(xv=avlast; cp=  *xv; xv++)
				argv[n++] = cp;
			argv[n] = 0;
		}
		if(saveargs || av<avlast || (exitval && !spawn))
		{
			if((pid=_spawnveg(shp,path,argv,envp,0)) < 0)
				return(-1);
			job_post(shp,pid,0);
			job_wait(pid);
			if(shp->exitval>exitval)
				exitval = shp->exitval;
			if(saveargs)
			{
				memcpy((void*)av,saveargs,n);
				free((void*)saveargs);
				saveargs = 0;
			}
		}
		else if(spawn && !sh_isoption(SH_PFSH))
		{
			shp->xargexit = exitval;
			if(saveargs)
				free((void*)saveargs);
			return(_spawnveg(shp,path,argv,envp,spawn>>1));
		}
		else
		{
			if(saveargs)