UNIX Process Control and IPC Methods
UNIX Process Control and IPC Methods
Module 4
Process Control (Continued..)
Syllabus Module 4
PROCESS IDENTIFIERS
#include <unistd.h>
pid_t getpid(void); Returns: process ID of calling process
pid_t getppid(void); Returns: parent process ID of calling process
uid_t getuid(void); Returns: real user ID of calling process
uid_t geteuid(void); Returns: effective user ID of calling process
gid_t getgid(void); Returns: real group ID of calling process
gid_t getegid(void); Returns: effective group ID of calling process
1/24/2023
Ashwini 2
Kamath,
fork FUNCTION
• An existing process can create a new one by calling the fork function.
#include <unistd.h>
pid_t fork(void);
• Returns: 0 in child, process ID of child in parent, -1 on error.
• The new process created by fork is called the child process.
• This function is called once but returns twice.
• The only difference in the returns is that the return value in the child is
0, whereas the return value in the parent is the process ID of the new
child.
• The reason the child's process ID is returned to the parent is that a
process can have more than one child, and there is no function that
allows a process to obtain the process IDs of its children.
• The reason fork returns 0 to the child is that a process can have only a
single parent, and the child can always call getppid to obtain the process
ID of its parent.
1/24/2023
Ashwini 3
Kamath,
• Process ID 0 is reserved for use by the kernel, so it's
not possible for 0 to be the process ID of a child.
• Both the child and the parent continue executing
with the instruction that follows the call to fork.
• The child is a copy of the parent.
• For example, the child gets a copy of the parent's
data space, heap, and stack.
• Note that this is a copy for the child; the parent and
the child do not share these portions of memory.
• The parent and the child share the text segment
1/24/2023
Ashwini 4
Kamath,
/* Program to demonstrate fork function Program name fork1.c */
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main( )
{
fork( );
printf(“Hello World\n”);
}
$ cc fork1.c
$ ./[Link]
Hello World
Hello World
Note : The statement Hello World is executed twice as both the child
and parent have executed that instruction.
1/24/2023
Ashwini 5
Kamath,
/* Program name fork2.c */
#include <stdio.h>
#include<unistd.h>
int j=20;
int main() Output:
{ Hello World
int pid, i=10; i=10, j=20
pid=fork( );
if(pid==0)
{
i++; j++;
_exit(0);
}
printf("Hello World\n");
printf("i=%d, j=%d",i,j);
return 0;
}
1/24/2023
Ashwini 6
Kamath,
/* Program name fork3.c */
#include<sys/types.h>
#include<unistd.h>
int main( )
{
printf(“Before fork: Parent process\n”);
fork( );
printf(“After fork\n”);
$ cc fork1.c
$ ./[Link]
Before fork: Parent process
After fork
After fork
1/24/2023
Ashwini 7
Kamath,
• Note: The statement Before fork: Parent process is
executed only once by the parent because it is called
before fork and statement After fork is executed twice by
child and parent
File Sharing
• Consider a process that has three different files opened
for standard input, standard output, and standard error.
On return from fork, we have the arrangement shown in
Figure 8.2.
1/24/2023
Ashwini 8
Kamath,
1/24/2023
Ashwini 9
Kamath,
• It is important that the parent and the child share the same file
offset.
• Consider a process that forks a child, then waits for the child to
complete.
• Assume that both processes write to standard output as part of
their normal processing.
• If the parent has its standard output redirected (by a shell, perhaps)
it is essential that the parent's file offset be updated by the child
when the child writes to standard output.
• In this case, the child can write to standard output while the parent
is waiting for it; on completion of the child, the parent can continue
writing to standard output, knowing that its output will be
appended to whatever the child wrote.
• If the parent and the child did not share the same file offset, this
type of interaction would be more difficult to accomplish and would
require explicit actions by the parent.
1/24/2023
Ashwini 10
Kamath,
There are two normal cases for handling the descriptors after a fork.
1. The parent waits for the child to complete. In this case, the parent
does not need to do anything with its descriptors. When the child
terminates, any of the shared descriptors that the child read from or
wrote to will have their file offsets updated accordingly.
2. Both the parent and the child go their own ways. Here, after the fork,
the parent closes the descriptors that it doesn't need, and the child
does the same thing. This way, neither interferes with the other's
open descriptors. This scenario is often the case with network
servers
There are numerous other properties of the parent that are inherited
by the child:
• Real user ID, real group ID, effective user ID, effective group ID
• Supplementary group IDs
• Process group ID
• Session ID
1/24/2023
Ashwini 11
Kamath,
• Controlling terminal
• The set-user-ID and set-group-ID flags
• Current working directory
• Root directory
• File mode creation mask
• Signal mask and dispositions
• The close-on-exec flag for any open file descriptors
• Environment
• Attached shared memory segments
• Memory mappings
• Resource limits
1/24/2023
Ashwini 12
Kamath,
The differences between the parent and child are
• The return value from fork
• The process IDs are different
• The two processes have different parent process IDs: the
parent process ID of the child is the parent; the parent
process ID of the parent doesn't change
• The child's tms_utime, tms_stime, tms_cutime, and
tms_cstime values are set to 0
• File locks set by the parent are not inherited by the child
• Pending alarms are cleared for the child
• The set of pending signals for the child is set to the empty set
1/24/2023
Ashwini 14
Kamath,
vfork FUNCTION
• The function vfork has the same calling sequence and same return
values as fork.
#include <unistd.h>
pid_t vfork(void);
• The vfork function is intended to create a new process when the
purpose of the new process is to exec a new program.
• The vfork function creates the new process, just like fork, without
copying the address space of the parent into the child, as the child
won't reference that address space; the child simply calls exec (or exit)
right after the vfork.
• Instead, while the child is running and until it calls either exec or
exit, the child runs in the address space of the parent.
1/24/2023
Ashwini 15
Kamath,
• This optimization provides an efficiency gain on some paged virtual-
memory implementations of the UNIX System.
• Another difference between the two functions is that vfork
guarantees that the child runs first, until the child calls exec or exit.
• When the child calls either of these functions, the parent resumes.
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int glob = 6; /* external variable in initialized data */
int main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
printf("before vfork\n"); /* we don't flush stdio */
1/24/2023
Ashwini 16
Kamath,
if ((pid = vfork()) < 0)
{
err_sys("vfork error");
}
else if (pid == 0)
{ /* child */
glob++; /* modify parent's variables */
var++;
_exit(0); /* child terminates */
}
/* Parent continues here. */
printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
exit(0);
}
Output:
$ ./[Link]
before vfork
pid = 29039, glob = 7, var = 89
1/24/2023
Ashwini 17
Kamath,
/* Program name vork2.c */
#include <stdio.h>
#include<unistd.h>
int j=20;
int main() Output:
{ Hello World
int pid, i=10; i=11, j=21
pid=fork( );
if(pid==0)
{
i++; j++;
_exit(0);
}
printf("Hello World\n");
printf("i=%d, j=%d",i,j);
return 0;
}
1/24/2023
Ashwini 18
Kamath,
exit FUNCTIONS
There are eight ways for a process to terminate. Normal
termination occurs in five ways:
1. Return from main
2. Calling exit
3. Calling _exit or _Exit
4. Return of the last thread from its start routine
5. Calling pthread_exit from the last thread
Abnormal termination occurs in three ways:
1. Calling abort
2. Receipt of a signal
3. Response of the last thread to a cancellation request
1/24/2023
Ashwini 19
Kamath,
Exit Functions
• Three functions terminate a program normally: _exit and _Exit,
which return to the kernel immediately, and exit, which
performs certain cleanup processing and then returns to the
kernel.
1/24/2023
Ashwini 20
Kamath,
wait AND waitpid FUNCTIONS
• When a process terminates, either normally or abnormally, the kernel
notifies the parent by sending the SIGCHLD signal to the parent.
• Because the termination of a child is an asynchronous event - it can
happen at any time while the parent is running - this signal is the
asynchronous notification from the kernel to the parent.
• The parent can choose to ignore this signal, or it can provide a
function that is called when the signal occurs: a signal handler.
1/24/2023
Ashwini 21
Kamath,
• Both return: process ID if OK, 0 (when using WNOHANG option
if child process status is not available to fetch), or -1 on error.
1/24/2023
Ashwini 23
Kamath,
#include <sys/wait.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
void pr_exit(int status)
{
if (WIFEXITED(status))
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? " (core file generated)" : "");
#else
"");
#endif
else if (WIFSTOPPED(status))
printf("child stopped, signal number = %d\n“, WSTOPSIG(status));
}
1/24/2023
Ashwini 24
Kamath,
int main(void)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0) /* child */
exit(7);
if (wait(&status) != pid) /* wait for child */
err_sys("wait error");
pr_exit(status); /* and print its status */
1/24/2023
Ashwini 26
Kamath,
• The interpretation of the pid argument for
waitpid depends on its value:
Pid Meaning
pid == 1 Waits for any child process. In this respect, waitpid is
equivalent to wait.
pid > 0 Waits for the child whose process ID equals pid.
pid < 1 Waits for any child whose process group ID equals the
absolute value of pid
1/24/2023
Ashwini 27
Kamath,
Macros to examine the termination status returned by wait and waitpid
Macro Description
WIFEXITED(status) True if status was returned for a child that terminated normally. In
this case, we can execute WEXITSTATUS (status)
to fetch the low-order 8 bits of the argument that the child passed to
exit, _exit,or _Exit.
WIFSIGNALED True if status was returned for a child that terminated abnormally, by
(status) receipt of a signal that it didn't catch. In this case, we can execute
WTERMSIG (status) to fetch the signal number that caused the
termination.
Additionally, some implementations (but not the Single UNIX
Specification) define the macro WCOREDUMP (status) that returns
true if a core file of the terminated process was generated.
WIFSTOPPED True if status was returned for a child that is currently stopped. In this
(status) case, we can execute WSTOPSIG (status) to fetch the signal number
that caused the child to stop.
WIFCONTINUED True if status was returned for a child that has been continued after a
(status) job control stop
1/24/2023
Ashwini 28
Kamath,
The options constants for waitpid
Constant Description
1/24/2023
Ashwini 30
Kamath,
Program to Avoid zombie processes by calling fork twice
#include <sys/wait.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0) /* first child */
{
if ( ( pid = fork() ) < 0 )
err_sys("fork error");
else if (pid > 0)
exit(0); /* parent from second fork == first child */
/* second child parent process is init after its parent called exit*/
sleep(2);
printf("second child, parent pid = %d\n", getppid());
exit(0);
}
1/24/2023
Ashwini 31
Kamath,
if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
err_sys("waitpid error");
exit(0);
}
Output:
$ ./[Link]
$ second child, parent pid = 1
1/24/2023
Ashwini 32
Kamath,
Waitid FUNCTION
• The waitid function is similar to waitpid, but provides extra
flexibility.
#include<sys/wait.h>
int waitid ( idtype_t idtype, id_t id, siginfo_t *infop, int options);
• Returns: 0 if OK, -1 on error
The idtype constants for waited are as follows:
Constant Description
P_PID Wait for a particular process: id contains the process ID of the
child to wait for.
P_PGID Wait for any child process in a particular process group: id
contains the process group ID of the
children to wait for.
P_ALL Wait for any child process: id is ignored.
1/24/2023
Ashwini 33
Kamath,
• The options argument is a bitwise OR of the flags as shown below:
these flags indicate which state changes the caller is interested in.
Constant Description
WCONTINUED Wait for a process that has previously stopped and has
been continued, and whose status has
Not yet been reported.
WEXITED Wait for processes that have exited.
WNOHANG Return immediately instead of blocking if there is no
child exit status available.
WNOWAIT Don't destroy the child exit status. The child's exit status
can be retrieved by a subsequent call to wait, waitid,or
waitpid
WSTOPPED Wait for a process that has stopped and whose status has
not yet been reported.
1/24/2023
Ashwini 34
Kamath,
wait3 AND wait4 FUNCTIONS
• The only feature provided by these two functions that isn't provided
by the wait, waitid, and waitpid functions is an additional argument
that allows the kernel to return a summary of the resources used by
the terminated process and all its child processes.
1/24/2023
Ashwini 35
Kamath,
• The resource information includes such statistics as
the amount of user CPU time, the amount of system
CPU time, number of page faults, number of signals
received etc.
• The resource information is available only for
terminated child process not for the process that
were stopped due to job control.
1/24/2023
Ashwini 36
Kamath,
RACE CONDITIONS
• A race condition occurs when multiple processes are trying to do
something with shared data and the final outcome depends on the
order in which the processes run.
• Example: The program below outputs two strings: one from the child
and one from the parent. The program contains a race condition
because the output depends on the order in which the processes are
run by the kernel and for how long each process runs.
#include<sys/wait.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
static void charatatime(char *);
int main(void)
{
pid_t pid;
1/24/2023
Ashwini 37
Kamath,
if ((pid = fork()) < 0)
err_sys("fork error"); Output:
else if (pid == 0) $ ./[Link]
charatatime("output from child\n"); ooutput from child
else utput from parent
charatatime("output from parent\n");
exit(0); $ ./[Link]
} ooutput from child
static void charatatime(char *str) utput from parent
{
char *ptr; $ ./[Link]
int c; output from child
setbuf(stdout, NULL); /* set unbuffered */ output from parent
for (ptr = str; (c = *ptr++) != 0; )
putc(c, stdout);
}
1/24/2023
Ashwini 38
Kamath,
program modification to avoid race condition
#include<sys/wait.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
static void charatatime(char *);
int main(void)
{
pid_t pid;
TELL_WAIT();
if ((pid = fork()) < 0)
err_sys("fork error");
1/24/2023
Ashwini 39
Kamath,
else if (pid == 0)
{
WAIT_PARENT(); /* parent goes first */ When we run this
charatatime("output from child\n"); program, the output is as
} we expect; there is no
else intermixing of output
{ from the two processes.
charatatime("output from parent\n");
TELL_CHILD(pid);
} $ ./[Link]
exit(0); output from parent
} output from child
1/24/2023
Ashwini 41
Kamath,
• There are 6 exec functions:
#include <unistd.h>
int execl (const char *pathname, const char *arg0, ... /* (char *) 0 */ );
int execle (const char *pathname, const char *arg0, … /* (char *) 0, char
*const envp[] */ );
int execve (const char *pathname, char *const argv[], char const envp[]);
int execlp (const char *filename, const char *arg0, ... /* (char *) 0 */ );
1/24/2023
Ashwini 42
Kamath,
Difference among six exec functions:
1. The first difference in these functions is that the first four take a
pathname argument, whereas the last two take a filename
argument. When a filename argument is specified
• If filename contains a slash, it is taken as a pathname.
• Otherwise, the executable file is searched for in the directories
specified by the PATH environment variable.
2. The next difference concerns the passing of the argument list (l
stands for list and v stands for vector).
• The functions execl, execlp, and execle require each of the
command-line arguments to the new program to be specified as
separate arguments.
• For the other three functions (execv, execvp, and execve), we have
to build an array of pointers to the arguments, and the address of
this array is the argument to these three functions
1/24/2023
Ashwini 43
Kamath,
3. The final difference is the passing of the environment
list to the new program.
• The two functions whose names end in an e (execle
and execve) allow us to pass a pointer to an array of
pointers to the environment strings.
• The other four functions, however, use the environ
variable in the calling process to copy the existing
environment for the new program.
1/24/2023
Ashwini 44
Kamath,
Function pathname filename Arg list Argv[ ] environ envp[ ]
execl * * *
execlp * * *
execle * * *
execv * * *
execvp * * *
execve * * *
(letter in p l v e
Name)
1/24/2023
Ashwini 45
Kamath,
Relationship of the six exec functions
1/24/2023
Ashwini 46
Kamath,
Process ID does not change after an exec, but the new program
inherits additional properties from the calling process:
• Process ID and parent process ID
• Real user ID and real group ID
• Supplementary group IDs
• Process group ID
• Session ID
• Controlling terminal
• Time left until alarm clock
• Current working directory
• Root directory
• File mode creation mask
• File locks
• Process signal mask
• Pending signals
• Resource limits
• Values for tms_utime, tms_stime, tms_cutime, and tms_cstime.
1/24/2023
Ashwini 47
Kamath,
#include<sys/wait.h>
char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0)
if (execle("/home/sar/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *)0,
env_init) <0)
err_sys("execle error");
if (waitpid(pid, NULL, 0) < 0)
err_sys("wait error");
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0)
if(execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
err_sys("execlp error");
exit(0);
} Ashwini Kamath, Asst. Prof., ISE, AIT
1/24/2023 48
chikmagalur
/*echoall program that exec function executes in previous code*/
#include<stdio.h>
int main(int argc, char *argv[])
{
int i;
char **ptr;
extern char **environ;
for (i = 0; i < argc; i++) /* echo all command-line args */
printf("argv[%d]: %s\n", i, argv[i]);
for (ptr = environ; *ptr != 0; ptr++) /* and all env strings */
printf("%s\n", *ptr);
exit(0);
}
1/24/2023
Ashwini 49
Kamath,
Output:
$ ./[Link]
argv[0]: echoall
argv[1]: myarg1
argv[2]: MY ARG2
USER=unknown
PATH=/tmp
$ argv[0]: echoall
argv[1]: only 1 arg
USER=sar
LOGNAME=sar
…. 47 more lines that aren't shown
SHELL=/bin/bash
HOME=/home/sar
1/24/2023
Ashwini 50
Kamath,
Overview of IPC Methods
Overview of IPC Methods
• INTRODUCTION
• IPC enables one application to control another application, and for several
applications to share the same data without interfering with one another.
IPC is required in all multiprocessing systems, but it is not generally
supported by single-process operating systems.
• The various forms of IPC that are supported on a UNIX system are as
follows :
1. Half duplex Pipes.
2. Full duplex Pipes.
3. FIFO’s
4. Named full duplex Pipes.
5. Message queues.
6. Shared memory.
7. Semaphores.
8. Sockets.
9. STREAMS.
• The first seven forms of IPC are usually restricted to IPC between processes
on the same host. The final two i.e. Sockets and STREAMS are the only two
that are generally supported for IPC between processes on different hosts.
Ashwini Kamath, Asst. Prof., ISE, AIT
1/20/2023 2
chikmagalur
Pipes
• Pipes are the oldest form of UNIX System IPC. Pipes have two
limitations.
1. Historically, they have been half duplex (i.e., data flows in only one
direction).
2. Pipes can be used only between processes that have a common
ancestor. Normally, a pipe is created by a process, that process
calls fork, and the pipe is used between the parent and the child.
• A pipe is created by calling the pipe function.
#include <unistd.h>
int pipe(int fd[2]);
Returns: 0 if OK, −1 on error
• Two file descriptors are returned through the fd argument: fd[0] is
open for reading, and fd[1] is open for writing. The output of fd[1] is
the input for fd[0].
Ashwini Kamath, Asst. Prof., ISE, AIT
1/20/2023 3
chikmagalur
• Two ways to picture a half-duplex pipe are shown in Figure 15.2.
The left half of the figure shows the two ends of the pipe connected
in a single process.
• The right half of the figure emphasizes that the data in the pipe
flows through the kernel.
FIFOs
• FIFOs are sometimes called named pipes. Pipes can be used
only between related processes when a common ancestor has
created the pipe.
IPC_SET Copy the following fields from the structure pointed to by buf to the
msqid_ds structure associated with this queue: msg_perm.uid,
msg_perm.gid, msg_perm.mode, and msg_qbytes.
IPC_RMID Remove the message queue from the system and any data still on the
queue. This removal is immediate. Any other process still using the
message queue will get an error of EIDRM on its next attempted
operation on the queue.
Ashwini Kamath, Asst. Prof., ISE, AIT
1/20/2023 26
chikmagalur
• Data is placed onto a message queue by calling msgsnd.
#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
• Returns: 0 if OK, −1 on error
• Each message is composed of a positive long integer type field, a
non-negative length (nbytes), and the actual data bytes
(corresponding to the length).
• Messages are always placed at the end of the queue.
• The ptr argument points to a long integer that contains the positive
integer message type, and it is immediately followed by the
message data. (There is no message data if nbytes is 0.)
• If the largest message we send is 512 bytes, we can define the
following structure:
struct mymesg
{
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
Ashwini Kamath, Asst. Prof., ISE, AIT
1/20/2023 27
chikmagalur
• The ptr argument is then a pointer to a mymesg structure.
The message type can be used by the receiver to fetch
messages in an order other than first in, first out.
• Messages are retrieved from a queue by msgrcv.
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type,
int flag);
• Returns: size of data portion of message if OK, −1 on error
• The type argument lets us specify which message we want.
type == 0 The first message on the queue is returned.
type > 0 The first message on the queue whose message type equals type is
returned.
type < 0 The first message on the queue whose message type is the lowest
value less than or equal to the absolute value of type is returned.
Parameters:
• shmid → ID from shmget()
• shmaddr → desired address (usually NULL)
• shmflg → 0 for read/write, or SHM_RDONLY
Return:
• On success → pointer to attached memory segment
• On error → (void *) -1
shmdt() — Detach Shared Memory Segment
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
Parameter: shmaddr → address returned by shmat()
Return: 0 on success, −1 on error
Synchronization
Shared memory provides no synchronization.
To avoid race conditions: Use semaphores (semget, semop)
Client–Server Properties
Basic Client–Server Relationship
• Simplest form: Client forks and execs the server.
• Two half-duplex pipes are created before fork() for two-way
communication.
• The server may be a set-user-ID program, giving it special
privileges (e.g., root).
• The server can determine the client’s real user ID since exec does
not change real IDs.
Open Server Concept
• The server can perform open() calls on behalf of the client.
• Allows extra permission checks beyond normal UNIX file
permissions.
• The server (with elevated privileges) decides if the client can
access a file based on the client’s real UID.
• Works fine for regular files, but cannot pass file descriptors back
to parent processes easily.
Daemon-Type Servers
• The server runs independently (daemon process) and
communicates via IPC mechanisms.
• Pipes cannot be used; must use named IPC like:
• FIFOs
• Message Queues
• Shared Memory
Using FIFOs
• For one-way communication (client → server), one well-
known FIFO is enough.
• For two-way communication, each client needs its own FIFO.
• Example: System V printer spooler (lp command → lpsched
daemon).
Using Message Queues
Two techniques:
1. Single Queue (shared by all clients):
• Clients send requests with type = 1 and include their PID.
• Server replies using type = client’s PID.
2. Separate Queue per Client:
• Each client creates a private queue (IPC_PRIVATE).
• Server has a well-known queue.
• Client includes its queue ID in the first message.
• Drawbacks:
• Waste of limited system-wide queues.
• Server must manage multiple queues — select() and
poll() don’t work with message queues.
Shared Memory and Synchronization
• Message queues can be replaced with shared memory segments
and semaphores/record locking for coordination.
Client Identification & Authentication
• Important when the server has special privileges (set-user-ID).
• Kernel IPC mechanisms don’t automatically identify the sender.
• msg_lspid gives the client’s PID, but not its effective UID — not
portable to obtain UID from PID.
#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
int msg_iovlen; /* elements in msg_iov[] */
void *msg_control; /* ancillary data */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
struct cmsghdr {
socklen_t cmsg_len; /* data length including header */
int cmsg_level; /* originating protocol (SOL_SOCKET) */
int cmsg_type; /* type (SCM_RIGHTS for fd passing) */
/* followed by unsigned char cmsg_data[] */
};
Steps
Example
• Client connects to server via UNIX-domain socket.
• Server opens a file (possibly with elevated privileges).
• Server calls send_fd() to transmit the open descriptor.
• Client receives it with recv_fd() and then can read()/write()
normally.
An Open Server – Version 1
• Build a server that performs privileged open() operations on
behalf of clients and passes back the open descriptor using
the mechanism above.
Server
• Runs with suitable privileges (e.g., set-user-ID root).
• Waits on a UNIX-domain socket for client requests.
• Each request contains the filename and open() flags.
• Calls open(pathname, oflag, mode) to obtain a descriptor.
• Returns the result:
• If success → uses send_fd() to pass descriptor.
• If failure → sends an error code.
Client
• Connects to the server socket.
• Sends filename and desired flags.
• Waits for either an error indication or a received descriptor
• On success, uses the descriptor for normal I/O.
for ( ; ; ) {
/* read request from client */
if ( (n = read(STDIN_FILENO, buf, MAXLINE)) <= 0 )
break;
buf[n-1] = 0; /* strip newline */
if ( (fd = open(buf, O_RDONLY)) < 0 )
send_err(STDOUT_FILENO, errno, "cannot open");
else
send_fd(STDOUT_FILENO, fd);
close(fd);
}
exit(0);
}
int open_client(const char *pathname, int oflag)
{
int sockfd, fd;
sockfd = cli_conn(SERVER_PATH); /* connect to server */
write(sockfd, pathname, strlen(pathname)+1);
fd = recv_fd(sockfd); /* get descriptor */
close(sockfd);
return fd;
}