Getting the Session ID: getsid()
getsid(pid) returns the session ID of the specified process. Pass 0 to get the session ID of the calling process.
The session ID equals the PID of the session leader. A new process inherits its parent’s session ID.
#define _XOPEN_SOURCE 500
#include <unistd.h>
pid_t getsid(pid_t pid);
/* pid == 0 → session ID of calling process
* Returns session ID on success, -1 on error (EPERM on some systems) */
Creating a New Session: setsid()
setsid() creates a brand new session. It is the key call for creating daemon processes.
When a non-group-leader process calls setsid(), three things happen simultaneously:
#include <unistd.h>
pid_t setsid(void);
/* Returns: new session ID on success, -1 on error (EPERM if caller is a group leader) */
Why a Process Group Leader Cannot Call setsid()
If the calling process is already a process group leader, setsid() fails with EPERM. This is a deliberate restriction.
If a group leader were allowed to call setsid(), it would create a new session while the other members of its process group remained in the old session. This would break the rule that all members of a process group must be in the same session.
Solution: Always fork() first, have the parent exit, then have the child call setsid(). The child gets a new unique PID which is different from any existing PGID, so it cannot be a group leader.
The call to setsid() is one of the first steps in making a daemon process. A daemon must have no controlling terminal so it does not receive terminal signals, and setsid() achieves that.
Code Example 1 — Creating a New Session (t_setsid)
/* t_setsid.c
* Demonstrates setsid() to create a new session.
* Verifies the new session has no controlling terminal by trying to open /dev/tty.
* Compile: gcc -o t_setsid t_setsid.c
*/
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(void)
{
printf("Before fork:\n");
printf(" PID=%ld PGID=%ld SID=%ld\n",
(long)getpid(), (long)getpgrp(), (long)getsid(0));
/* Step 1: fork so child is NOT a process group leader */
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); }
if (pid != 0) {
/* Parent exits — child is now an orphan (adopted by init) */
printf("Parent exiting...\n");
exit(EXIT_SUCCESS);
}
/* --- CHILD CONTINUES --- */
printf("\nChild (PID=%ld) calling setsid()...\n", (long)getpid());
/* Step 2: child calls setsid() — cannot be group leader here */
pid_t sid = setsid();
if (sid == -1) {
perror("setsid");
exit(EXIT_FAILURE);
}
printf("After setsid():\n");
printf(" PID=%ld PGID=%ld SID=%ld\n",
(long)getpid(), (long)getpgrp(), (long)getsid(0));
/* Note: PID == PGID == SID — all equal after setsid() */
/* Step 3: Verify no controlling terminal by opening /dev/tty */
int fd = open("/dev/tty", O_RDWR);
if (fd == -1) {
/* ENXIO means no controlling terminal — expected! */
printf("open(\"/dev/tty\") failed: %s (errno=%d)\n",
strerror(errno), errno);
printf(" → Confirmed: new session has NO controlling terminal\n");
} else {
printf("open(\"/dev/tty\") succeeded (unexpected)\n");
close(fd);
}
return 0;
}
/*
* Expected output:
* Before fork:
* PID=12352 PGID=12352 SID=12243 ← shell's PID is SID
* Parent exiting...
* Child (PID=12353) calling setsid()...
* After setsid():
* PID=12353 PGID=12353 SID=12353 ← PID == PGID == SID
* open("/dev/tty") failed: No such device or address (errno=6)
* → Confirmed: new session has NO controlling terminal
*/
Code Example 2 — Verifying setsid() Fails for Group Leader
/* setsid_group_leader_fail.c
* Demonstrates that setsid() fails with EPERM if the caller is a process group leader.
* Compile: gcc -o sgl setsid_group_leader_fail.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(void)
{
printf("=== Testing setsid() restrictions ===\n\n");
/* Case 1: main process is typically already a group leader */
printf("Case 1: Calling setsid() directly (process is group leader)\n");
printf(" PID=%ld PGID=%ld\n", (long)getpid(), (long)getpgrp());
if (getpid() == getpgrp()) {
printf(" We ARE the group leader — setsid() should fail.\n");
}
pid_t result = setsid();
if (result == -1) {
printf(" setsid() returned -1, errno=%d (%s)\n",
errno, strerror(errno)); /* EPERM */
} else {
printf(" setsid() succeeded (we were not a group leader)\n");
}
printf("\nCase 2: fork() first, then setsid() in child\n");
pid_t child = fork();
if (child == -1) { perror("fork"); return 1; }
if (child == 0) {
/*
* The child has a new PID which is guaranteed to differ from
* any existing PGID, so the child is NOT a group leader.
*/
printf(" Child PID=%ld PGID=%ld\n",
(long)getpid(), (long)getpgrp());
printf(" Child is%s a group leader.\n",
(getpid() == getpgrp()) ? "" : " NOT");
pid_t sid = setsid();
if (sid == -1) {
perror(" child setsid");
} else {
printf(" child setsid() succeeded! SID=%ld\n", (long)sid);
}
return 0;
}
wait(NULL);
return 0;
}
/*
* Expected output:
* Case 1: Calling setsid() directly (process is group leader)
* PID=5000 PGID=5000
* We ARE the group leader — setsid() should fail.
* setsid() returned -1, errno=1 (Operation not permitted)
*
* Case 2: fork() first, then setsid() in child
* Child PID=5001 PGID=5000
* Child is NOT a group leader.
* child setsid() succeeded! SID=5001
*/
