162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Use the core scheduling prctl() to test core scheduling cookies control.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2021 Oracle and/or its affiliates.
662306a36Sopenharmony_ci * Author: Chris Hyser <chris.hyser@oracle.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This library is free software; you can redistribute it and/or modify it
1062306a36Sopenharmony_ci * under the terms of version 2.1 of the GNU Lesser General Public License as
1162306a36Sopenharmony_ci * published by the Free Software Foundation.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This library is distributed in the hope that it will be useful, but WITHOUT
1462306a36Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1562306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
1662306a36Sopenharmony_ci * for more details.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public License
1962306a36Sopenharmony_ci * along with this library; if not, see <http://www.gnu.org/licenses>.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define _GNU_SOURCE
2362306a36Sopenharmony_ci#include <sys/eventfd.h>
2462306a36Sopenharmony_ci#include <sys/wait.h>
2562306a36Sopenharmony_ci#include <sys/types.h>
2662306a36Sopenharmony_ci#include <sched.h>
2762306a36Sopenharmony_ci#include <sys/prctl.h>
2862306a36Sopenharmony_ci#include <unistd.h>
2962306a36Sopenharmony_ci#include <time.h>
3062306a36Sopenharmony_ci#include <errno.h>
3162306a36Sopenharmony_ci#include <stdio.h>
3262306a36Sopenharmony_ci#include <stdlib.h>
3362306a36Sopenharmony_ci#include <string.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if __GLIBC_PREREQ(2, 30) == 0
3662306a36Sopenharmony_ci#include <sys/syscall.h>
3762306a36Sopenharmony_cistatic pid_t gettid(void)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	return syscall(SYS_gettid);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#ifndef PR_SCHED_CORE
4462306a36Sopenharmony_ci#define PR_SCHED_CORE			62
4562306a36Sopenharmony_ci# define PR_SCHED_CORE_GET		0
4662306a36Sopenharmony_ci# define PR_SCHED_CORE_CREATE		1 /* create unique core_sched cookie */
4762306a36Sopenharmony_ci# define PR_SCHED_CORE_SHARE_TO		2 /* push core_sched cookie to pid */
4862306a36Sopenharmony_ci# define PR_SCHED_CORE_SHARE_FROM	3 /* pull core_sched cookie to pid */
4962306a36Sopenharmony_ci# define PR_SCHED_CORE_MAX		4
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MAX_PROCESSES 128
5362306a36Sopenharmony_ci#define MAX_THREADS   128
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic const char USAGE[] = "cs_prctl_test [options]\n"
5662306a36Sopenharmony_ci"    options:\n"
5762306a36Sopenharmony_ci"	-P  : number of processes to create.\n"
5862306a36Sopenharmony_ci"	-T  : number of threads per process to create.\n"
5962306a36Sopenharmony_ci"	-d  : delay time to keep tasks alive.\n"
6062306a36Sopenharmony_ci"	-k  : keep tasks alive until keypress.\n";
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cienum pid_type {PIDTYPE_PID = 0, PIDTYPE_TGID, PIDTYPE_PGID};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciconst int THREAD_CLONE_FLAGS = CLONE_THREAD | CLONE_SIGHAND | CLONE_FS | CLONE_VM | CLONE_FILES;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct child_args {
6762306a36Sopenharmony_ci	int num_threads;
6862306a36Sopenharmony_ci	int pfd[2];
6962306a36Sopenharmony_ci	int cpid;
7062306a36Sopenharmony_ci	int thr_tids[MAX_THREADS];
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct child_args procs[MAX_PROCESSES];
7462306a36Sopenharmony_cistatic int num_processes = 2;
7562306a36Sopenharmony_cistatic int need_cleanup = 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int _prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,
7862306a36Sopenharmony_ci		  unsigned long arg5)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	int res;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	res = prctl(option, arg2, arg3, arg4, arg5);
8362306a36Sopenharmony_ci	printf("%d = prctl(%d, %ld, %ld, %ld, %lx)\n", res, option, (long)arg2, (long)arg3,
8462306a36Sopenharmony_ci	       (long)arg4, arg5);
8562306a36Sopenharmony_ci	return res;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define STACK_SIZE (1024 * 1024)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define handle_error(msg) __handle_error(__FILE__, __LINE__, msg)
9162306a36Sopenharmony_cistatic void __handle_error(char *fn, int ln, char *msg)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int pidx;
9462306a36Sopenharmony_ci	printf("(%s:%d) - ", fn, ln);
9562306a36Sopenharmony_ci	perror(msg);
9662306a36Sopenharmony_ci	if (need_cleanup) {
9762306a36Sopenharmony_ci		for (pidx = 0; pidx < num_processes; ++pidx)
9862306a36Sopenharmony_ci			kill(procs[pidx].cpid, 15);
9962306a36Sopenharmony_ci		need_cleanup = 0;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci	exit(EXIT_FAILURE);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void handle_usage(int rc, char *msg)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	puts(USAGE);
10762306a36Sopenharmony_ci	puts(msg);
10862306a36Sopenharmony_ci	putchar('\n');
10962306a36Sopenharmony_ci	exit(rc);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic unsigned long get_cs_cookie(int pid)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	unsigned long long cookie;
11562306a36Sopenharmony_ci	int ret;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	ret = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid, PIDTYPE_PID,
11862306a36Sopenharmony_ci		    (unsigned long)&cookie);
11962306a36Sopenharmony_ci	if (ret) {
12062306a36Sopenharmony_ci		printf("Not a core sched system\n");
12162306a36Sopenharmony_ci		return -1UL;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return cookie;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int child_func_thread(void __attribute__((unused))*arg)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	while (1)
13062306a36Sopenharmony_ci		usleep(20000);
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void create_threads(int num_threads, int thr_tids[])
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	void *child_stack;
13762306a36Sopenharmony_ci	pid_t tid;
13862306a36Sopenharmony_ci	int i;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (i = 0; i < num_threads; ++i) {
14162306a36Sopenharmony_ci		child_stack = malloc(STACK_SIZE);
14262306a36Sopenharmony_ci		if (!child_stack)
14362306a36Sopenharmony_ci			handle_error("child stack allocate");
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		tid = clone(child_func_thread, child_stack + STACK_SIZE, THREAD_CLONE_FLAGS, NULL);
14662306a36Sopenharmony_ci		if (tid == -1)
14762306a36Sopenharmony_ci			handle_error("clone thread");
14862306a36Sopenharmony_ci		thr_tids[i] = tid;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int child_func_process(void *arg)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct child_args *ca = (struct child_args *)arg;
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	close(ca->pfd[0]);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	create_threads(ca->num_threads, ca->thr_tids);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ret = write(ca->pfd[1], &ca->thr_tids, sizeof(int) * ca->num_threads);
16262306a36Sopenharmony_ci	if (ret == -1)
16362306a36Sopenharmony_ci		printf("write failed on pfd[%d] - error (%s)\n",
16462306a36Sopenharmony_ci			ca->pfd[1], strerror(errno));
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	close(ca->pfd[1]);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	while (1)
16962306a36Sopenharmony_ci		usleep(20000);
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic unsigned char child_func_process_stack[STACK_SIZE];
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_civoid create_processes(int num_processes, int num_threads, struct child_args proc[])
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	pid_t cpid;
17862306a36Sopenharmony_ci	int i, ret;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	for (i = 0; i < num_processes; ++i) {
18162306a36Sopenharmony_ci		proc[i].num_threads = num_threads;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (pipe(proc[i].pfd) == -1)
18462306a36Sopenharmony_ci			handle_error("pipe() failed");
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		cpid = clone(child_func_process, child_func_process_stack + STACK_SIZE,
18762306a36Sopenharmony_ci			     SIGCHLD, &proc[i]);
18862306a36Sopenharmony_ci		proc[i].cpid = cpid;
18962306a36Sopenharmony_ci		close(proc[i].pfd[1]);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (i = 0; i < num_processes; ++i) {
19362306a36Sopenharmony_ci		ret = read(proc[i].pfd[0], &proc[i].thr_tids, sizeof(int) * proc[i].num_threads);
19462306a36Sopenharmony_ci		if (ret == -1)
19562306a36Sopenharmony_ci			printf("read failed on proc[%d].pfd[0] error (%s)\n",
19662306a36Sopenharmony_ci				i, strerror(errno));
19762306a36Sopenharmony_ci		close(proc[i].pfd[0]);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_civoid disp_processes(int num_processes, struct child_args proc[])
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int i, j;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	printf("tid=%d, / tgid=%d / pgid=%d: %lx\n", gettid(), getpid(), getpgid(0),
20662306a36Sopenharmony_ci	       get_cs_cookie(getpid()));
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < num_processes; ++i) {
20962306a36Sopenharmony_ci		printf("    tid=%d, / tgid=%d / pgid=%d: %lx\n", proc[i].cpid, proc[i].cpid,
21062306a36Sopenharmony_ci		       getpgid(proc[i].cpid), get_cs_cookie(proc[i].cpid));
21162306a36Sopenharmony_ci		for (j = 0; j < proc[i].num_threads; ++j) {
21262306a36Sopenharmony_ci			printf("        tid=%d, / tgid=%d / pgid=%d: %lx\n", proc[i].thr_tids[j],
21362306a36Sopenharmony_ci			       proc[i].cpid, getpgid(0), get_cs_cookie(proc[i].thr_tids[j]));
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci	puts("\n");
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int errors;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci#define validate(v) _validate(__LINE__, v, #v)
22262306a36Sopenharmony_civoid _validate(int line, int val, char *msg)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	if (!val) {
22562306a36Sopenharmony_ci		++errors;
22662306a36Sopenharmony_ci		printf("(%d) FAILED: %s\n", line, msg);
22762306a36Sopenharmony_ci	} else {
22862306a36Sopenharmony_ci		printf("(%d) PASSED: %s\n", line, msg);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciint main(int argc, char *argv[])
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int keypress = 0;
23562306a36Sopenharmony_ci	int num_threads = 3;
23662306a36Sopenharmony_ci	int delay = 0;
23762306a36Sopenharmony_ci	int res = 0;
23862306a36Sopenharmony_ci	int pidx;
23962306a36Sopenharmony_ci	int pid;
24062306a36Sopenharmony_ci	int opt;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, ":hkT:P:d:")) != -1) {
24362306a36Sopenharmony_ci		switch (opt) {
24462306a36Sopenharmony_ci		case 'P':
24562306a36Sopenharmony_ci			num_processes = (int)strtol(optarg, NULL, 10);
24662306a36Sopenharmony_ci			break;
24762306a36Sopenharmony_ci		case 'T':
24862306a36Sopenharmony_ci			num_threads = (int)strtoul(optarg, NULL, 10);
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		case 'd':
25162306a36Sopenharmony_ci			delay = (int)strtol(optarg, NULL, 10);
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		case 'k':
25462306a36Sopenharmony_ci			keypress = 1;
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		case 'h':
25762306a36Sopenharmony_ci			printf(USAGE);
25862306a36Sopenharmony_ci			exit(EXIT_SUCCESS);
25962306a36Sopenharmony_ci		default:
26062306a36Sopenharmony_ci			handle_usage(20, "unknown option");
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (num_processes < 1 || num_processes > MAX_PROCESSES)
26562306a36Sopenharmony_ci		handle_usage(1, "Bad processes value");
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (num_threads < 1 || num_threads > MAX_THREADS)
26862306a36Sopenharmony_ci		handle_usage(2, "Bad thread value");
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (keypress)
27162306a36Sopenharmony_ci		delay = -1;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	srand(time(NULL));
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* put into separate process group */
27662306a36Sopenharmony_ci	if (setpgid(0, 0) != 0)
27762306a36Sopenharmony_ci		handle_error("process group");
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	printf("\n## Create a thread/process/process group hiearchy\n");
28062306a36Sopenharmony_ci	create_processes(num_processes, num_threads, procs);
28162306a36Sopenharmony_ci	need_cleanup = 1;
28262306a36Sopenharmony_ci	disp_processes(num_processes, procs);
28362306a36Sopenharmony_ci	validate(get_cs_cookie(0) == 0);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	printf("\n## Set a cookie on entire process group\n");
28662306a36Sopenharmony_ci	if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, 0, PIDTYPE_PGID, 0) < 0)
28762306a36Sopenharmony_ci		handle_error("core_sched create failed -- PGID");
28862306a36Sopenharmony_ci	disp_processes(num_processes, procs);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	validate(get_cs_cookie(0) != 0);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* get a random process pid */
29362306a36Sopenharmony_ci	pidx = rand() % num_processes;
29462306a36Sopenharmony_ci	pid = procs[pidx].cpid;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	validate(get_cs_cookie(0) == get_cs_cookie(pid));
29762306a36Sopenharmony_ci	validate(get_cs_cookie(0) == get_cs_cookie(procs[pidx].thr_tids[0]));
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	printf("\n## Set a new cookie on entire process/TGID [%d]\n", pid);
30062306a36Sopenharmony_ci	if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, PIDTYPE_TGID, 0) < 0)
30162306a36Sopenharmony_ci		handle_error("core_sched create failed -- TGID");
30262306a36Sopenharmony_ci	disp_processes(num_processes, procs);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	validate(get_cs_cookie(0) != get_cs_cookie(pid));
30562306a36Sopenharmony_ci	validate(get_cs_cookie(pid) != 0);
30662306a36Sopenharmony_ci	validate(get_cs_cookie(pid) == get_cs_cookie(procs[pidx].thr_tids[0]));
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	printf("\n## Copy the cookie of current/PGID[%d], to pid [%d] as PIDTYPE_PID\n",
30962306a36Sopenharmony_ci	       getpid(), pid);
31062306a36Sopenharmony_ci	if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, pid, PIDTYPE_PID, 0) < 0)
31162306a36Sopenharmony_ci		handle_error("core_sched share to itself failed -- PID");
31262306a36Sopenharmony_ci	disp_processes(num_processes, procs);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	validate(get_cs_cookie(0) == get_cs_cookie(pid));
31562306a36Sopenharmony_ci	validate(get_cs_cookie(pid) != 0);
31662306a36Sopenharmony_ci	validate(get_cs_cookie(pid) != get_cs_cookie(procs[pidx].thr_tids[0]));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	printf("\n## Copy cookie from a thread [%d] to current/PGID [%d] as PIDTYPE_PID\n",
31962306a36Sopenharmony_ci	       procs[pidx].thr_tids[0], getpid());
32062306a36Sopenharmony_ci	if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, procs[pidx].thr_tids[0],
32162306a36Sopenharmony_ci		   PIDTYPE_PID, 0) < 0)
32262306a36Sopenharmony_ci		handle_error("core_sched share from thread failed -- PID");
32362306a36Sopenharmony_ci	disp_processes(num_processes, procs);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	validate(get_cs_cookie(0) == get_cs_cookie(procs[pidx].thr_tids[0]));
32662306a36Sopenharmony_ci	validate(get_cs_cookie(pid) != get_cs_cookie(procs[pidx].thr_tids[0]));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	printf("\n## Copy cookie from current [%d] to current as pidtype PGID\n", getpid());
32962306a36Sopenharmony_ci	if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, 0, PIDTYPE_PGID, 0) < 0)
33062306a36Sopenharmony_ci		handle_error("core_sched share to self failed -- PGID");
33162306a36Sopenharmony_ci	disp_processes(num_processes, procs);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	validate(get_cs_cookie(0) == get_cs_cookie(pid));
33462306a36Sopenharmony_ci	validate(get_cs_cookie(pid) != 0);
33562306a36Sopenharmony_ci	validate(get_cs_cookie(pid) == get_cs_cookie(procs[pidx].thr_tids[0]));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	validate(_prctl(PR_SCHED_CORE, PR_SCHED_CORE_MAX, 0, PIDTYPE_PGID, 0) < 0
33862306a36Sopenharmony_ci		&& errno == EINVAL);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	validate(_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, 0, PIDTYPE_PGID, 1) < 0
34162306a36Sopenharmony_ci		&& errno == EINVAL);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (errors) {
34462306a36Sopenharmony_ci		printf("TESTS FAILED. errors: %d\n", errors);
34562306a36Sopenharmony_ci		res = 10;
34662306a36Sopenharmony_ci	} else {
34762306a36Sopenharmony_ci		printf("SUCCESS !!!\n");
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (keypress)
35162306a36Sopenharmony_ci		getchar();
35262306a36Sopenharmony_ci	else
35362306a36Sopenharmony_ci		sleep(delay);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	for (pidx = 0; pidx < num_processes; ++pidx)
35662306a36Sopenharmony_ci		kill(procs[pidx].cpid, 15);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return res;
35962306a36Sopenharmony_ci}
360