Use when writing low-level system software in C requiring file I/O, process management, signals, and system calls.
Writes low-level C code for file I/O, process management, signals, and system calls.
/plugin marketplace add thebushidocollective/han/plugin install c@hanThis skill is limited to using the following tools:
Master C systems programming including file I/O, process management, inter-process communication, signals, and system calls for writing robust low-level system software.
File descriptors are integers that represent open files in Unix-like systems. Standard file descriptors:
0 - Standard input (STDIN_FILENO)1 - Standard output (STDOUT_FILENO)2 - Standard error (STDERR_FILENO)#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
int fd;
char buffer[1024];
ssize_t bytes_read, bytes_written;
// Open file for reading
fd = open("input.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// Read from file
bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read == -1) {
perror("read");
close(fd);
return 1;
}
buffer[bytes_read] = '\0';
// Close file
if (close(fd) == -1) {
perror("close");
return 1;
}
printf("Read %zd bytes: %s\n", bytes_read, buffer);
return 0;
}
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int write_file(const char *filename, const char *data) {
int fd;
ssize_t bytes_written;
size_t len = strlen(data);
// Open file for writing, create if doesn't exist, truncate if exists
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return -1;
}
// Write data
bytes_written = write(fd, data, len);
if (bytes_written == -1) {
perror("write");
close(fd);
return -1;
}
if ((size_t)bytes_written != len) {
fprintf(stderr, "Partial write: %zd of %zu bytes\n",
bytes_written, len);
close(fd);
return -1;
}
if (close(fd) == -1) {
perror("close");
return -1;
}
return 0;
}
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int fd;
char buffer[10];
off_t offset;
fd = open("data.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// Seek to byte 10 from start
offset = lseek(fd, 10, SEEK_SET);
if (offset == -1) {
perror("lseek");
close(fd);
return 1;
}
// Read 10 bytes
if (read(fd, buffer, sizeof(buffer)) == -1) {
perror("read");
close(fd);
return 1;
}
// Seek to end of file
offset = lseek(fd, 0, SEEK_END);
printf("File size: %lld bytes\n", (long long)offset);
close(fd);
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
pid_t pid;
int status;
printf("Parent process (PID: %d)\n", getpid());
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// Child process
printf("Child process (PID: %d, Parent: %d)\n",
getpid(), getppid());
sleep(2);
printf("Child exiting\n");
return 42;
} else {
// Parent process
printf("Parent created child (PID: %d)\n", pid);
// Wait for child to exit
if (waitpid(pid, &status, 0) == -1) {
perror("waitpid");
return 1;
}
if (WIFEXITED(status)) {
printf("Child exited with status: %d\n",
WEXITSTATUS(status));
}
}
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// Child process - execute ls command
char *args[] = {"ls", "-l", "/tmp", NULL};
char *envp[] = {NULL};
execve("/bin/ls", args, envp);
// If execve returns, an error occurred
perror("execve");
return 1;
} else {
// Parent process - wait for child
int status;
waitpid(pid, &status, 0);
printf("Child completed\n");
}
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
void print_process_info(void) {
printf("Process ID (PID): %d\n", getpid());
printf("Parent Process ID (PPID): %d\n", getppid());
printf("Process Group ID (PGID): %d\n", getpgrp());
printf("User ID (UID): %d\n", getuid());
printf("Effective User ID (EUID): %d\n", geteuid());
printf("Group ID (GID): %d\n", getgid());
printf("Effective Group ID (EGID): %d\n", getegid());
}
int main(void) {
print_process_info();
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
int main(void) {
int pipefd[2];
pid_t pid;
char buffer[100];
// Create pipe
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// Child process - writes to pipe
close(pipefd[0]); // Close read end
const char *msg = "Hello from child process!";
write(pipefd[1], msg, strlen(msg) + 1);
close(pipefd[1]);
return 0;
} else {
// Parent process - reads from pipe
close(pipefd[1]); // Close write end
read(pipefd[0], buffer, sizeof(buffer));
printf("Parent received: %s\n", buffer);
close(pipefd[0]);
wait(NULL);
}
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
// Writer process
void fifo_writer(void) {
const char *fifo_path = "/tmp/myfifo";
int fd;
const char *msg = "Message through FIFO";
// Create FIFO if it doesn't exist
if (mkfifo(fifo_path, 0666) == -1) {
perror("mkfifo");
// Continue if already exists
}
fd = open(fifo_path, O_WRONLY);
if (fd == -1) {
perror("open");
return;
}
write(fd, msg, strlen(msg) + 1);
close(fd);
}
// Reader process
void fifo_reader(void) {
const char *fifo_path = "/tmp/myfifo";
int fd;
char buffer[100];
fd = open(fifo_path, O_RDONLY);
if (fd == -1) {
perror("open");
return;
}
read(fd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
close(fd);
unlink(fifo_path);
}
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
volatile sig_atomic_t keep_running = 1;
void signal_handler(int signum) {
if (signum == SIGINT) {
printf("\nReceived SIGINT (Ctrl+C)\n");
keep_running = 0;
} else if (signum == SIGTERM) {
printf("Received SIGTERM\n");
keep_running = 0;
}
}
int main(void) {
struct sigaction sa;
// Setup signal handler
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("sigaction SIGINT");
return 1;
}
if (sigaction(SIGTERM, &sa, NULL) == -1) {
perror("sigaction SIGTERM");
return 1;
}
printf("Running... Press Ctrl+C to stop\n");
while (keep_running) {
printf("Working...\n");
sleep(1);
}
printf("Cleaning up and exiting\n");
return 0;
}
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
pid_t pid;
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// Child process - pause until signal received
printf("Child waiting for signal...\n");
pause();
printf("Child received signal\n");
return 0;
} else {
// Parent process - send signal after delay
printf("Parent sleeping...\n");
sleep(2);
printf("Parent sending SIGUSR1 to child\n");
if (kill(pid, SIGUSR1) == -1) {
perror("kill");
return 1;
}
wait(NULL);
printf("Child process completed\n");
}
return 0;
}
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
sigset_t set, oldset;
// Initialize signal set
sigemptyset(&set);
sigaddset(&set, SIGINT);
// Block SIGINT
if (sigprocmask(SIG_BLOCK, &set, &oldset) == -1) {
perror("sigprocmask");
return 1;
}
printf("SIGINT blocked for 5 seconds (Ctrl+C won't work)\n");
sleep(5);
// Unblock SIGINT
if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1) {
perror("sigprocmask");
return 1;
}
printf("SIGINT unblocked (Ctrl+C will work now)\n");
sleep(5);
return 0;
}
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <time.h>
void demonstrate_system_calls(void) {
struct stat file_stat;
char cwd[1024];
time_t current_time;
// Get current working directory
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("Current directory: %s\n", cwd);
}
// Get file information
if (stat("/etc/passwd", &file_stat) == 0) {
printf("File size: %lld bytes\n",
(long long)file_stat.st_size);
printf("Permissions: %o\n", file_stat.st_mode & 0777);
printf("Last modified: %s", ctime(&file_stat.st_mtime));
}
// Get current time
current_time = time(NULL);
printf("Current time: %s", ctime(¤t_time));
// Get process times
printf("Process times:\n");
printf(" Ticks per second: %ld\n", sysconf(_SC_CLK_TCK));
}
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
int list_directory(const char *path) {
DIR *dir;
struct dirent *entry;
dir = opendir(path);
if (dir == NULL) {
perror("opendir");
return -1;
}
printf("Contents of %s:\n", path);
errno = 0;
while ((entry = readdir(dir)) != NULL) {
printf(" %s (type: %d)\n", entry->d_name, entry->d_type);
}
if (errno != 0) {
perror("readdir");
closedir(dir);
return -1;
}
if (closedir(dir) == -1) {
perror("closedir");
return -1;
}
return 0;
}
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
void demonstrate_error_handling(void) {
int fd;
// Attempt to open non-existent file
fd = open("/nonexistent/file.txt", O_RDONLY);
if (fd == -1) {
int saved_errno = errno; // Save errno immediately
printf("Error code: %d\n", saved_errno);
printf("Error message (strerror): %s\n", strerror(saved_errno));
// Alternative using perror
errno = saved_errno;
perror("open");
// Check specific error
if (saved_errno == ENOENT) {
fprintf(stderr, "File does not exist\n");
} else if (saved_errno == EACCES) {
fprintf(stderr, "Permission denied\n");
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int copy_file(const char *src, const char *dst) {
int src_fd = -1, dst_fd = -1;
char buffer[4096];
ssize_t bytes_read, bytes_written;
int ret = -1;
// Open source file
src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
fprintf(stderr, "Cannot open source file %s: %s\n",
src, strerror(errno));
goto cleanup;
}
// Open destination file
dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd == -1) {
fprintf(stderr, "Cannot open destination file %s: %s\n",
dst, strerror(errno));
goto cleanup;
}
// Copy data
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
fprintf(stderr, "Write error: %s\n", strerror(errno));
goto cleanup;
}
}
if (bytes_read == -1) {
fprintf(stderr, "Read error: %s\n", strerror(errno));
goto cleanup;
}
ret = 0; // Success
cleanup:
if (src_fd != -1) close(src_fd);
if (dst_fd != -1) close(dst_fd);
return ret;
}
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *thread_function(void *arg) {
int *value = (int *)arg;
printf("Thread running with value: %d\n", *value);
sleep(1);
*value = 100;
return value;
}
int main(void) {
pthread_t thread;
int value = 42;
void *result;
if (pthread_create(&thread, NULL, thread_function, &value) != 0) {
perror("pthread_create");
return 1;
}
printf("Main thread waiting...\n");
if (pthread_join(thread, &result) != 0) {
perror("pthread_join");
return 1;
}
printf("Thread returned: %d\n", *(int *)result);
return 0;
}
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(void) {
const char *name = "/my_shm";
const size_t size = 4096;
int shm_fd;
void *ptr;
// Create shared memory object
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
perror("shm_open");
return 1;
}
// Set size
if (ftruncate(shm_fd, size) == -1) {
perror("ftruncate");
return 1;
}
// Map shared memory
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
return 1;
}
// Write to shared memory
strcpy((char *)ptr, "Hello, shared memory!");
// Cleanup
munmap(ptr, size);
close(shm_fd);
shm_unlink(name);
return 0;
}
Always Check Return Values: Every system call can fail. Check return values and handle errors appropriately using errno, perror, or strerror.
Use Signal-Safe Functions: In signal handlers, only use async-signal-safe functions. Avoid printf, malloc, and other non-reentrant functions.
Close File Descriptors: Always close file descriptors when done to prevent resource leaks. Use cleanup patterns with goto for complex error handling.
Handle Partial I/O: read() and write() may transfer fewer bytes than requested. Always check return values and loop when necessary.
Use O_CLOEXEC: When opening files, use O_CLOEXEC flag to prevent file descriptor leaks across exec() calls.
Proper Process Cleanup: Always wait for child processes using wait() or waitpid() to prevent zombie processes.
Use sigaction Over signal: The sigaction() interface is more portable and reliable than the older signal() function.
Avoid Race Conditions: Be careful with signals and file operations. Use proper synchronization mechanisms like mutexes or semaphores.
Set Signal Masks Carefully: Block signals during critical sections to prevent race conditions, but restore the original mask afterward.
Use POSIX Standards: Prefer POSIX-compliant functions over system-specific extensions for better portability across Unix-like systems.
Ignoring errno: Not checking errno after system call failures or checking it when no error occurred can lead to misleading error messages.
File Descriptor Leaks: Forgetting to close file descriptors in error paths causes resource exhaustion over time.
Zombie Processes: Not waiting for child processes leaves zombie processes that consume system resources until parent exits.
Signal Handler Complexity: Using non-async-signal-safe functions in signal handlers can cause deadlocks or crashes.
Assuming Complete I/O: Assuming read() or write() transfers all requested bytes can lead to data corruption or loss.
Race Conditions with fork: File descriptors and signals can cause race conditions when forking. Use careful synchronization.
Incorrect exec() Usage: Forgetting the NULL terminator in argument arrays for exec() family functions causes undefined behavior.
Buffer Overflows: Not checking sizes when reading into buffers can cause security vulnerabilities and crashes.
Mixing I/O Methods: Mixing low-level I/O (open, read, write) with stdio (fopen, fread, fwrite) on the same file can cause buffering issues.
Hardcoded Paths: Using hardcoded paths instead of environment variables or configuration files reduces portability and flexibility.
Use C systems programming when you need to:
This skill is essential for systems programmers, embedded developers, and anyone working close to the operating system level.
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Activates when the user asks about Agent Skills, wants to find reusable AI capabilities, needs to install skills, or mentions skills for Claude. Use for discovering, retrieving, and installing skills.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.