646 lines
29 KiB
HTML
646 lines
29 KiB
HTML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE html
|
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html lang="en-us" xml:lang="en-us">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<meta name="security" content="public" />
|
|
<meta name="Robots" content="index,follow" />
|
|
<meta http-equiv="PICS-Label" content='(PICS-1.1 "http://www.icra.org/ratingsv02.html" l gen true r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true r (n 0 s 0 v 0 l 0) "http://www.classify.org/safesurf/" l gen true r (SS~~000 1))' />
|
|
<meta name="DC.Type" content="reference" />
|
|
<meta name="DC.Title" content="Examples: Using the spawn process and wait for child process APIs" />
|
|
<meta name="abstract" content="These two examples illustrate programs that use a parent/child relationship." />
|
|
<meta name="description" content="These two examples illustrate programs that use a parent/child relationship." />
|
|
<meta name="DC.Relation" scheme="URI" content="apiexmp.htm" />
|
|
<meta name="DC.Relation" scheme="URI" content="../apis/spawnu.htm" />
|
|
<meta name="copyright" content="(C) Copyright IBM Corporation 1998, 2006" />
|
|
<meta name="DC.Rights.Owner" content="(C) Copyright IBM Corporation 1998, 2006" />
|
|
<meta name="DC.Format" content="XHTML" />
|
|
<meta name="DC.Identifier" content="apiexuspro" />
|
|
<meta name="DC.Language" content="en-us" />
|
|
<!-- All rights reserved. Licensed Materials Property of IBM -->
|
|
<!-- US Government Users Restricted Rights -->
|
|
<!-- Use, duplication or disclosure restricted by -->
|
|
<!-- GSA ADP Schedule Contract with IBM Corp. -->
|
|
<link rel="stylesheet" type="text/css" href="./ibmdita.css" />
|
|
<link rel="stylesheet" type="text/css" href="./ic.css" />
|
|
<title>Examples: Using the spawn process and wait for child process APIs</title>
|
|
</head>
|
|
<body id="apiexuspro"><a name="apiexuspro"><!-- --></a>
|
|
<!-- Java sync-link --><script language="Javascript" src="../rzahg/synch.js" type="text/javascript"></script>
|
|
<h1 class="topictitle1">Examples: Using the spawn process and wait for child process APIs</h1>
|
|
<div><p>These two examples illustrate programs that use a parent/child
|
|
relationship.</p>
|
|
<div class="section"><p>See the QlgSpawn--Spawn Process (using NLS-enabled path name)
|
|
API for an example of supplying parameters in any CCSID.</p>
|
|
<div class="note"><span class="notetitle">Note:</span> Read the <a href="codedisclaimer.htm">Code license and disclaimer information</a> for important legal information.</div>
|
|
</div>
|
|
<div class="section" id="apiexuspro__HDRPARPGM"><a name="apiexuspro__HDRPARPGM"><!-- --></a><h4 class="sectiontitle">Parent program</h4><p>This program acts
|
|
as a parent to a child program (see <a href="#apiexuspro__HDRCHLPGM">Child program</a>).</p>
|
|
<p>This
|
|
program demonstrates the use of the spawn() function and the wait() and waitpid()
|
|
functions in a parent/child relationship. The use of file descriptors, the
|
|
creation of a new process group, arguments passed from parent to child, and
|
|
environment variables are demonstrated. The parent program uses spawn() in
|
|
three different ways.</p>
|
|
<p>Use the Create C Module (CRTCMOD) and the Create
|
|
Program (CRTPGM) commands to create this program (see <a href="#apiexuspro__HDRCPARCLD">Creating the parent and child programs</a>).</p>
|
|
<p>Call
|
|
this program with no parameters (see <a href="#apiexuspro__hdrcallpar">Calling the parent program</a>).</p>
|
|
<pre>/*******************************************************************/
|
|
/*******************************************************************/
|
|
/* */
|
|
/* FUNCTION: This program acts as a parent to a child program. */
|
|
/* */
|
|
/* LANGUAGE: ILE C */
|
|
/* */
|
|
/* APIs USED: putenv(), spawn(), wait(), waitpid() */
|
|
/* */
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <spawn.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#define MAP_NUM 5
|
|
#define ARGV_NUM 6
|
|
#define ENVP_NUM 2
|
|
#define CHILD_PGM "QGPL/CHILD"
|
|
|
|
extern char **environ;
|
|
|
|
/* This is a parent program that will use spawn() in 3 different */
|
|
/* ways for 3 different children. A file is created that is */
|
|
/* written to, both by the parent and the 3 children. The end result*/
|
|
/* of the file will look something like the following: */
|
|
/* Parent writes Child writes */
|
|
/* ------------- --------------------------------------- */
|
|
/* 1 argv[0] getppid() getpgrp() getpid() */
|
|
/* 2 argv[0] getppid() getpgrp() getpid() */
|
|
/* 3 argv[0] getppid() getpgrp() getpid() */
|
|
/* The parent uses wait() or waitpid() to wait for a given child to */
|
|
/* return and to retrieve the resulting status of the child when it */
|
|
/* does return. */
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int rc; /* API return code */
|
|
int fd, fd_read; /* parent file descriptors */
|
|
char fd_str[4]; /* file descriptor string */
|
|
char f_path_name[] = "A_File"; /* file pathname */
|
|
int buf_int; /* write(), read() buffer */
|
|
char buf_pgm_name[22]; /* read() program name buffer */
|
|
char spw_path[] = "/QSYS.LIB/QGPL.LIB/CHILD.PGM";
|
|
/* spawn() *path */
|
|
int spw_fd_count; /* spawn() fd_count */
|
|
int spw_fd_map[MAP_NUM];
|
|
/* spawn() fd_map[] */
|
|
struct inheritance spw_inherit; /* spawn() *inherit */
|
|
char *spw_argv[ARGV_NUM];
|
|
/* spawn() *argv[] */
|
|
char *spw_envp[ENVP_NUM];
|
|
/* spawn() *envp[] */
|
|
int seq_num; /* sequence number */
|
|
char seq_num_str[4]; /* sequence number string */
|
|
pid_t pid; /* parent pid */
|
|
char pid_str[11]; /* parent pid string */
|
|
pid_t pgrp; /* parent process group */
|
|
char pgrp_str[11]; /* parent process group string */
|
|
pid_t spw_child_pid[3]; /* 3 spawn() child pid */
|
|
pid_t wt_child_pid[3]; /* 3 wait()/waitpid() child pid */
|
|
int wt_stat_loc[3];
|
|
/* 3 wait()/waitpid() *stat_loc*/
|
|
int wt_pid_opt = 0; /* waitpid() option */
|
|
char env_return_val[16];
|
|
/* environ var "return_val=" */
|
|
|
|
memset(&spw_inherit,0x00,sizeof(spw_inherit));
|
|
|
|
/* Get the pid and pgrp for the parent. */
|
|
pid = getpid();
|
|
pgrp = getpgrp();
|
|
|
|
/* Format the pid and pgrp value into null-terminated strings. */
|
|
sprintf(pid_str, "%d", pid);
|
|
sprintf(pgrp_str, "%d", pgrp);
|
|
|
|
/* Create a file and maintain the file descriptor. */
|
|
fd = creat(f_path_name, S_IRWXU);
|
|
if (fd == -1)
|
|
{
|
|
printf("FAILURE: creat() with errno = %d\n",errno);
|
|
return -1;
|
|
}
|
|
|
|
/* Format the file descriptor into null-terminated string. */
|
|
sprintf(fd_str, "%d", fd);
|
|
|
|
/* Set the spawn() child arguments that are common for each */
|
|
/* child. */
|
|
/* NOTE: The child will always get argv[0] in the */
|
|
/* LIBRARY/PROGRAM notation, but the */
|
|
/* spawn() argv[0] (spw_argv[0] */
|
|
/* in this case) must be non-NULL in order to allow additional */
|
|
/* arguments. For this example, the character pointer spw_path */
|
|
/* was chosen. */
|
|
/* NOTE: The parent pid and the parent process group are passed */
|
|
/* to the child for demonstration purposes only. */
|
|
spw_argv[0] = spw_path;
|
|
spw_argv[1] = pid_str;
|
|
spw_argv[2] = pgrp_str;
|
|
spw_argv[4] = fd_str;
|
|
spw_argv[5] = NULL;
|
|
|
|
/* Write a '1' out to the file. */
|
|
buf_int = 1;
|
|
write(fd, &buf_int, sizeof(int));
|
|
|
|
/* The 1st spawn() will use simple inheritance for file */
|
|
/* descriptors (fd_map[] value is NULL). */
|
|
spw_fd_count = 0;
|
|
spw_inherit.pgroup = 0;
|
|
seq_num = 1;
|
|
sprintf(seq_num_str, "%d", seq_num);
|
|
spw_argv[3] = seq_num_str;
|
|
spw_envp[0] = NULL;
|
|
spw_child_pid[0] = spawn(spw_path, spw_fd_count, NULL, &spw_inherit,
|
|
spw_argv, spw_envp);
|
|
if (spw_child_pid[0] == -1)
|
|
{
|
|
printf("FAILURE: spawn() #1 with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* NOTE: The parent can continue processing while the child is */
|
|
/* also processing. In this example, though, the parent will */
|
|
/* simply /* wait() until the child finishes processing. */
|
|
|
|
/* Issue wait() in order to wait for the child to return. */
|
|
wt_child_pid[0] = wait(&wt_stat_loc[0]);
|
|
if (wt_child_pid[0] == -1)
|
|
{
|
|
printf("FAILURE: wait() #1 with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* Check to ensure the child's pid returned from spawn() is the */
|
|
/* same as the child's pid returned from wait(), for which */
|
|
/* status was returned. */
|
|
if ( (spw_child_pid[0] != wt_child_pid[0]) )
|
|
printf("FAILURE: spawn() #1 and wait() #1 pid not the same\n");
|
|
|
|
/* Check to ensure the child did not encounter an error */
|
|
/* condition. */
|
|
if (WIFEXITED(wt_stat_loc[0]))
|
|
{
|
|
if (WEXITSTATUS(wt_stat_loc[0]) != 1)
|
|
printf("FAILURE: wait() exit status = %d\n",
|
|
WEXITSTATUS(wt_stat_loc[0]));
|
|
}
|
|
else
|
|
printf("FAILURE: unknown child #1 status\n");
|
|
|
|
/* Write a '2' out to the file. */
|
|
buf_int = 2;
|
|
write(fd, &buf_int, sizeof(int));
|
|
|
|
/* The 2nd spawn() will use mapping for the file descriptor, */
|
|
/* along with the inheritance option to create a new process */
|
|
/* group for the child. */
|
|
spw_fd_count = 1;
|
|
spw_fd_map[0] = fd;
|
|
spw_inherit.pgroup = SPAWN_NEWPGROUP;
|
|
seq_num = 2;
|
|
sprintf(seq_num_str, "%d", seq_num);
|
|
spw_argv[3] = seq_num_str;
|
|
spw_envp[0] = NULL;
|
|
spw_child_pid[1] = spawn(spw_path, spw_fd_count, spw_fd_map,
|
|
&spw_inherit, spw_argv, spw_envp);
|
|
if (spw_child_pid[1] == -1)
|
|
{
|
|
printf("FAILURE: spawn() #2 with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* NOTE: The parent can continue processing while the child is */
|
|
/* also processing. In this example, though, the parent will */
|
|
/* simply waitpid() until the child finishes processing. */
|
|
|
|
/* Issue waitpid() in order to wait for the child to return. */
|
|
wt_child_pid[1] = waitpid(spw_child_pid[1], &wt_stat_loc[1],
|
|
wt_pid_opt);
|
|
if (wt_child_pid[1] == -1)
|
|
{
|
|
printf("FAILURE: waitpid() #2 with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* Check to ensure the child's pid returned from spawn() is the */
|
|
/* same as the child's pid returned from waitpid(), for which */
|
|
/* status was returned. */
|
|
if ( (spw_child_pid[1] != wt_child_pid[1]) )
|
|
printf("FAILURE: spawn() #2 and waitpid() #2 pid not same\n");
|
|
|
|
/* Check to ensure the child did not encounter an error */
|
|
/* condition. */
|
|
if (WIFEXITED(wt_stat_loc[1]))
|
|
{
|
|
if (WEXITSTATUS(wt_stat_loc[1]) != 2)
|
|
printf("FAILURE: waitpid() exit status = %d\n",
|
|
WEXITSTATUS(wt_stat_loc[1]));
|
|
}
|
|
else
|
|
printf("FAILURE: unknown child #2 status\n");
|
|
|
|
/* Write a '3' out to the file. */
|
|
buf_int = 3;
|
|
write(fd, &buf_int, sizeof(int));
|
|
|
|
/* The 3rd spawn() will use mapping for the file descriptors */
|
|
/* with some file descriptors designated as being closed */
|
|
/* (SPAWN_FDCLOSED) and the same parent file descriptor mapped */
|
|
/* to more than one child file descriptor. In addition, an */
|
|
/* environment variable will be set and used by the child. */
|
|
spw_fd_count = 5;
|
|
spw_fd_map[0] = SPAWN_FDCLOSED;
|
|
spw_fd_map[1] = SPAWN_FDCLOSED;
|
|
spw_fd_map[2] = fd;
|
|
spw_fd_map[3] = SPAWN_FDCLOSED;
|
|
spw_fd_map[4] = fd;
|
|
spw_inherit.pgroup = 0;
|
|
seq_num = 3;
|
|
sprintf(seq_num_str, "%d", seq_num);
|
|
spw_argv[3] = seq_num_str;
|
|
strcpy(env_return_val,"return_val=3");
|
|
rc = putenv(env_return_val);
|
|
if (rc < 0)
|
|
{
|
|
printf("FAILURE: putenv() with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
spw_child_pid[2] = spawn(spw_path, spw_fd_count, spw_fd_map,
|
|
&spw_inherit, spw_argv, environ);
|
|
if (spw_child_pid[2] == -1)
|
|
{
|
|
printf("FAILURE: spawn() #3 with errno = %d\n",errno);
|
|
close(fd);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* The parent no longer needs to use the file descriptor, so it */
|
|
/* can close it, now that it has issued spawn(). */
|
|
rc = close(fd);
|
|
if (rc != 0)
|
|
printf("FAILURE: close(fd) with errno = %d\n",errno);
|
|
|
|
/* NOTE: The parent can continue processing while the child is */
|
|
/* also processing. In this example, though, the parent will */
|
|
/* simply wait() until the child finishes processing. */
|
|
|
|
/* Issue wait() in order to wait for the child to return. */
|
|
wt_child_pid[2] = wait(&wt_stat_loc[2]);
|
|
if (wt_child_pid[2] == -1)
|
|
{
|
|
printf("FAILURE: wait() #3 with errno = %d\n",errno);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* Check to ensure the child's pid returned from spawn() is the */
|
|
/* same as the child's pid returned from wait(), for which */
|
|
/* status was returned. */
|
|
if ( (spw_child_pid[2] != wt_child_pid[2]) )
|
|
printf("FAILURE: spawn() #3 and wait() #3 pid not the same\n");
|
|
|
|
/* Check to ensure the child did not encounter an error */
|
|
/* condition. */
|
|
if (WIFEXITED(wt_stat_loc[2]))
|
|
{
|
|
if (WEXITSTATUS(wt_stat_loc[2]) != 3)
|
|
printf("FAILURE: wait() exit status = %d\n",
|
|
WEXITSTATUS(wt_stat_loc[2]));
|
|
}
|
|
else
|
|
printf("FAILURE: unknown child #3 status\n");
|
|
|
|
/* Open the file for read to verify what the child wrote. */
|
|
fd_read = open(f_path_name, O_RDONLY);
|
|
if (fd_read == -1)
|
|
{
|
|
printf("FAILURE: open() for read with errno = %d\n",errno);
|
|
unlink(f_path_name);
|
|
return -1;
|
|
}
|
|
|
|
/* Verify what child #1 wrote. */
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != 1) )
|
|
printf("FAILURE: read() #1\n");
|
|
memset(buf_pgm_name,0x00,sizeof(buf_pgm_name));
|
|
rc = read(fd_read, buf_pgm_name, strlen(CHILD_PGM));
|
|
if ( (rc != strlen(CHILD_PGM)) ||
|
|
(strcmp(buf_pgm_name,CHILD_PGM) != 0) )
|
|
printf("FAILURE: read() child #1 argv[0]\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != pid) )
|
|
printf("FAILURE: read() child #1 getppid()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != pgrp) )
|
|
printf("FAILURE: read() child #1 getpgrp()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != spw_child_pid[0]) ||
|
|
(buf_int != wt_child_pid[0]) )
|
|
printf("FAILURE: read() child #1 getpid()\n");
|
|
|
|
/* Verify what child #2 wrote. */
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != 2) )
|
|
printf("FAILURE: read() #2\n");
|
|
memset(buf_pgm_name,0x00,sizeof(buf_pgm_name));
|
|
rc = read(fd_read, buf_pgm_name, strlen(CHILD_PGM));
|
|
if ( (rc != strlen(CHILD_PGM)) ||
|
|
(strcmp(buf_pgm_name,CHILD_PGM) != 0) )
|
|
printf("FAILURE: read() child #2 argv[0]\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != pid) )
|
|
printf("FAILURE: read() child #2 getppid()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int == pgrp) )
|
|
printf("FAILURE: read() child #2 getpgrp()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != spw_child_pid[1]) ||
|
|
(buf_int != wt_child_pid[1]) )
|
|
printf("FAILURE: read() child #2 getpid()\n");
|
|
|
|
/* Verify what child #3 wrote. */
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != 3) )
|
|
printf("FAILURE: read() #3\n");
|
|
memset(buf_pgm_name,0x00,sizeof(buf_pgm_name));
|
|
rc = read(fd_read, buf_pgm_name, strlen(CHILD_PGM));
|
|
if ( (rc != strlen(CHILD_PGM)) ||
|
|
(strcmp(buf_pgm_name,CHILD_PGM) != 0) )
|
|
printf("FAILURE: read() child #3 argv[0]\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != pid) )
|
|
printf("FAILURE: read() child #3 getppid()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != pgrp) )
|
|
printf("FAILURE: read() child #3 getpgrp()\n");
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if ( (rc != sizeof(int)) || (buf_int != spw_child_pid[2]) ||
|
|
(buf_int != wt_child_pid[2]) )
|
|
printf("FAILURE: read() child #3 getpid()\n");
|
|
|
|
/* Attempt one more read() to ensure there is no more data. */
|
|
rc = read(fd_read, &buf_int, sizeof(int));
|
|
if (rc != 0)
|
|
printf("FAILURE: read() past end of data\n");
|
|
|
|
/* The parent no longer needs to use the read() file descriptor, */
|
|
/* so it can close it. */
|
|
rc = close(fd_read);
|
|
if (rc != 0)
|
|
printf("FAILURE: close(fd_read) with errno = %d\n",errno);
|
|
|
|
/* Attempt one more wait() to ensure there are no more children. */
|
|
wt_child_pid[0] = wait(&wt_stat_loc[0]);
|
|
if ( (wt_child_pid[0] != -1) || (errno != ECHILD) )
|
|
printf("FAILURE: ECHILD wait()\n");
|
|
|
|
/* Clean up by performing unlink(). */
|
|
rc = unlink(f_path_name);
|
|
if (rc != 0)
|
|
{
|
|
printf("FAILURE: unlink() with errno = %d\n",errno);
|
|
return -1;
|
|
}
|
|
printf("completed successfully\n");
|
|
return 0;
|
|
}</pre>
|
|
</div>
|
|
<div class="section" id="apiexuspro__HDRCHLPGM"><a name="apiexuspro__HDRCHLPGM"><!-- --></a><h4 class="sectiontitle">Child program</h4><p>This program acts as
|
|
a child to a parent program (see <a href="#apiexuspro__HDRPARPGM">Parent program</a>).
|
|
This program demonstrates how a child program uses characteristics expressed
|
|
through the use of spawn() in the parent program. The use of file descriptors,
|
|
the creation of a new process group, arguments passed from the parent, and
|
|
environment variables are demonstrated. The child program handles three distinct
|
|
calls through the use of one of its arguments.</p>
|
|
<p>Use the CRTCMOD and CRTPGM
|
|
commands to create this program (see <a href="#apiexuspro__HDRCPARCLD">Creating the parent and child programs</a>).</p>
|
|
<p>This
|
|
program is called by the spawn() function from the parent program. The program
|
|
name must be CHILD and must be created into library QGPL, as indicated by
|
|
the parent program. This program is not to be called directly.</p>
|
|
<pre>/*******************************************************************/
|
|
/*******************************************************************/
|
|
/* */
|
|
/* FUNCTION: This program acts as a child to a parent program. */
|
|
/* */
|
|
/* LANGUAGE: ILE C */
|
|
/* */
|
|
/* APIs USED: getenv(), getpid(), getppid(), getpgrp() */
|
|
/* */
|
|
/*******************************************************************/
|
|
/*******************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
/* This is a child program that gets control from a parent program */
|
|
/* that issues spawn(). This particular child program expects the */
|
|
/* following 5 arguments (all are null-terminated strings): */
|
|
/* argv[0] - child program name */
|
|
/* argv[1] - parent pid (for demonstration only) */
|
|
/* argv[2] - parent process group (for demonstration only) */
|
|
/* argv[3] - sequence number */
|
|
/* argv[4] - parent file descriptor */
|
|
/* If the child program encounters an error, it returns with a value */
|
|
/* greater than 50. If the parent uses wait() or waitpid(), this */
|
|
/* return value can be interrogated using the WIFEXITED and */
|
|
/* WEXITSTATUS macros on the resulting wait() or waitpid() */
|
|
/* *stat_loc field. */
|
|
int main(int argc, char *argv[])
|
|
{
|
|
pid_t p_pid; /* parent pid argv[1] */
|
|
pid_t p_pgrp; /* parent process group argv[2] */
|
|
int seq_num; /* parent sequence num argv[3] */
|
|
int fd; /* parent file desc argv[4] */
|
|
int rc; /* API return code */
|
|
pid_t pid; /* getpid() - child pid */
|
|
pid_t ppid; /* getppid() - parent pid */
|
|
pid_t pgrp; /* getpgrp() - process group */
|
|
char *env_return_val; /* environ var for "return_val" */
|
|
|
|
/* Get the pid, ppid, and pgrp for the child. */
|
|
pid = getpid();
|
|
ppid = getppid();
|
|
pgrp = getpgrp();
|
|
|
|
/* Verify 5 parameters were passed to the child. */
|
|
if (argc != 5)
|
|
return 60;
|
|
|
|
/* Since the parameters passed to the child using spawn() are */
|
|
/* pointers to strings, convert the parent pid, parent process */
|
|
/* group, sequence number, and the file descriptor from strings */
|
|
/* to integers. */
|
|
p_pid = atoi(argv[1]);
|
|
p_pgrp = atoi(argv[2]);
|
|
seq_num = atoi(argv[3]);
|
|
fd = atoi(argv[4]);
|
|
|
|
/* Verify the getpid() value of the parent is the same as the */
|
|
/* getppid() value of the child. */
|
|
if (p_pid != ppid)
|
|
return 61;
|
|
|
|
/* If the sequence number is 1, simple inheritance was used in */
|
|
/* this case. First, verify the getpgrp() value of the parent */
|
|
/* is the same as the getpgrp() value of the child. Next, the */
|
|
/* child will use the file descriptor passed in to write the */
|
|
/* child's values for argv[0], getppid(), getpgrp(), */
|
|
/* and getpid(). Finally, the child returns, which will satisfy */
|
|
/* the parent's wait() or waitpid(). */
|
|
if (seq_num == 1)
|
|
{
|
|
if (p_pgrp != pgrp)
|
|
return 70;
|
|
rc = write(fd, argv[0], strlen(argv[0]));
|
|
if (rc != strlen(argv[0]))
|
|
return 71;
|
|
rc = write(fd, &ppid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 72;
|
|
rc = write(fd, &pgrp, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 73;
|
|
rc = write(fd, &pid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 74;
|
|
return seq_num;
|
|
}
|
|
|
|
/* If the sequence number is 2, file descriptor mapping was used */
|
|
/* in this case. In addition, an inheritance option was used to */
|
|
/* indicate this child will create a new process group. First, */
|
|
/* verify the getpgrp() value of the parent is different than */
|
|
/* the getpgrp() value of the child. Next, the child will use */
|
|
/* a literal value of '0' as the file descriptor (instead of the */
|
|
/* parent's file descriptor passed in) since a known mapping was */
|
|
/* performed by the parent. This literal is used to write the */
|
|
/* child's values for argv[0], getppid(), getpgrp(), */
|
|
/* and getpid(). Finally, the child returns, which will satisfy */
|
|
/* the parent's wait() or waitpid(). */
|
|
else if (seq_num == 2)
|
|
{
|
|
if (p_pgrp == pgrp)
|
|
return 80;
|
|
rc = write(0, argv[0], strlen(argv[0]));
|
|
if (rc != strlen(argv[0]))
|
|
return 81;
|
|
rc = write(0, &ppid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 82;
|
|
rc = write(0, &pgrp, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 83;
|
|
rc = write(0, &pid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 84;
|
|
return seq_num;
|
|
}
|
|
|
|
/* If the sequence number is 3, file descriptor mapping was used */
|
|
/* in this case. In addition, an environment variable by the */
|
|
/* name of "return_val" was set with the desired return value. */
|
|
/* First, verify the getpgrp() value of the parent is the same */
|
|
/* as the getpgrp() value of the child. Next, the child will */
|
|
/* use literal values of '2' and '4' as the file descriptor */
|
|
/* (instead of the parent's file descriptor passed in) since a */
|
|
/* known mapping was performed by the parent. These literals */
|
|
/* are used to write the child's values for argv[0], getppid(), */
|
|
/* getpgrp(), and getpid(). Finally, getenv() is performed to */
|
|
/* retrieve the desired value to use on return, which will */
|
|
/* satisfy the parent's wait() or waitpid(). */
|
|
else if (seq_num == 3)
|
|
{
|
|
if (p_pgrp != pgrp)
|
|
return 90;
|
|
rc = write(4, argv[0], strlen(argv[0]));
|
|
if (rc != strlen(argv[0]))
|
|
return 91;
|
|
rc = write(2, &ppid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 92;
|
|
rc = write(4, &pgrp, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 93;
|
|
rc = write(2, &pid, sizeof(pid_t));
|
|
if (rc != sizeof(pid_t))
|
|
return 94;
|
|
env_return_val = getenv("return_val");
|
|
return (atoi(env_return_val));
|
|
}
|
|
|
|
/* If the sequence number is an unexpected value, return */
|
|
/* indicating an error. */
|
|
else
|
|
return 99;
|
|
}</pre>
|
|
</div>
|
|
<div class="section" id="apiexuspro__HDRCPARCLD"><a name="apiexuspro__HDRCPARCLD"><!-- --></a><h4 class="sectiontitle">Creating the parent and child programs</h4><p>The
|
|
following examples show how to create the example programs (<a href="#apiexuspro__HDRPARPGM">Parent program</a> and <a href="#apiexuspro__HDRCHLPGM">Child program</a>). These examples assume that the source
|
|
for the parent program is member PARENT in the file QGPL/QCSRC and the source
|
|
for the child program is member CHILD in the file QGPL/QCSRC.</p>
|
|
<p><strong>Create
|
|
the parent module:</strong></p>
|
|
<pre>CRTCMOD MODULE(QGPL/PARENT)
|
|
SRCFILE(QGPL/QCSRC)
|
|
SRCMBR(PARENT)
|
|
TEXT('Example Parent')</pre>
|
|
<p><strong>Create the child module:</strong></p>
|
|
<pre>CRTCMOD MODULE(QGPL/CHILD)
|
|
SRCFILE(QGPL/QCSRC)
|
|
SRCMBR(CHILD)
|
|
TEXT('Example Child')</pre>
|
|
<p><strong>Create the parent program:</strong></p>
|
|
<pre>CRTPGM PGM(QGPL/PARENT)</pre>
|
|
<p><strong>Create the child program:</strong></p>
|
|
<pre>CRTPGM PGM(QGPL/CHILD)</pre>
|
|
</div>
|
|
<div class="section" id="apiexuspro__hdrcallpar"><a name="apiexuspro__hdrcallpar"><!-- --></a><h4 class="sectiontitle">Calling the parent program</h4><p>The following
|
|
example shows how to start the example programs:</p>
|
|
<pre>CALL PGM(QGPL/PARENT)</pre>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="familylinks">
|
|
<div class="parentlink"><strong>Parent topic:</strong> <a href="apiexmp.htm" title="Contains example programs that use APIs and exit programs.">Examples: APIs</a></div>
|
|
</div>
|
|
<div class="relref"><strong>Related reference</strong><br />
|
|
<div><a href="../apis/spawnu.htm">QlgSpawn</a></div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html> |