#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>

#define CHECK(x) do { if (!(x)) { fprintf(stderr,"failed: %s at line %d: %s\n",#x,__LINE__,strerror(errno)); exit(1); } } while(0)
static void fd_demo(void) {
 char path[]="./fd-lab-XXXXXX",a=0,b=0,c=0;
 int fd=mkstemp(path); CHECK(fd>=0);
 CHECK(write(fd,"ABC",3)==3); CHECK(lseek(fd,0,SEEK_SET)==0);
 int alias=dup(fd); CHECK(alias>=0);
 int independent=open(path,O_RDONLY|O_CLOEXEC); CHECK(independent>=0);
 CHECK(read(fd,&a,1)==1); CHECK(read(alias,&b,1)==1);
 CHECK(read(independent,&c,1)==1);
 printf("offsets: first=%c dup=%c independent=%c\n",a,b,c);
 CHECK(unlink(path)==0);
 CHECK(read(fd,&c,1)==1); printf("after unlink: next=%c path_exists=%s\n",c,access(path,F_OK)==0?"yes":"no");
 CHECK(close(independent)==0); CHECK(close(alias)==0); CHECK(close(fd)==0);
}
static void memory_demo(void) {
 char path[]="./map-lab-XXXXXX",c=0;
 int fd=mkstemp(path); CHECK(fd>=0); CHECK(unlink(path)==0);
 long page=sysconf(_SC_PAGESIZE); CHECK(page>0); CHECK(ftruncate(fd,page)==0);
 CHECK(pwrite(fd,"A",1,0)==1);
 char *p=mmap(NULL,(size_t)page,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0); CHECK(p!=MAP_FAILED);
 p[0]='P'; CHECK(pread(fd,&c,1,0)==1); printf("MAP_PRIVATE: mapping=%c file=%c\n",p[0],c); CHECK(munmap(p,(size_t)page)==0);
 p=mmap(NULL,(size_t)page,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); CHECK(p!=MAP_FAILED);
 p[0]='S'; CHECK(msync(p,(size_t)page,MS_SYNC)==0); CHECK(pread(fd,&c,1,0)==1);
 printf("MAP_SHARED: mapping=%c file=%c\n",p[0],c);
 CHECK(munmap(p,(size_t)page)==0); CHECK(close(fd)==0);
}
static void epoll_demo(void) {
 int p[2]; CHECK(pipe2(p,O_NONBLOCK|O_CLOEXEC)==0);
 char b[8]; errno=0; ssize_t n=read(p[0],b,sizeof b);
 printf("empty nonblocking pipe: EAGAIN=%s\n",n==-1&&errno==EAGAIN?"yes":"no");
 CHECK(n==-1&&errno==EAGAIN);
 int ep=epoll_create1(EPOLL_CLOEXEC); CHECK(ep>=0);
 struct epoll_event ev={.events=EPOLLIN|EPOLLET,.data.fd=p[0]},out;
 CHECK(epoll_ctl(ep,EPOLL_CTL_ADD,p[0],&ev)==0);
 CHECK(write(p[1],"AB",2)==2); CHECK(epoll_wait(ep,&out,1,1000)==1);
 CHECK(read(p[0],b,1)==1); printf("ET first read=%c\n",b[0]);
 int count=epoll_wait(ep,&out,1,0); CHECK(count==0);
 printf("ET without new write: events=%d\n",count);
 CHECK(read(p[0],b,1)==1); printf("ET drain remaining=%c\n",b[0]);
 errno=0; CHECK(read(p[0],b,sizeof b)==-1&&errno==EAGAIN);
 CHECK(close(p[1])==0); CHECK(read(p[0],b,sizeof b)==0);
 puts("all writers closed: read=0 (EOF)");
 CHECK(close(p[0])==0); CHECK(close(ep)==0);
}
static int shared_value=1;
static void *thread_fn(void *unused) { (void)unused; shared_value=9; return NULL; }
static void process_thread_demo(void) {
 int p[2]; CHECK(pipe(p)==0); int value=1; pid_t pid=fork(); CHECK(pid>=0);
 if(pid==0) { close(p[0]); value=7; if(write(p[1],&value,sizeof value)!=(ssize_t)sizeof value)_exit(2); close(p[1]); _exit(0); }
 CHECK(close(p[1])==0); int child=0,status=0;
 CHECK(read(p[0],&child,sizeof child)==(ssize_t)sizeof child); CHECK(close(p[0])==0);
 CHECK(waitpid(pid,&status,0)==pid); CHECK(WIFEXITED(status)&&WEXITSTATUS(status)==0);
 printf("fork: parent=%d child=%d\n",value,child);
 pthread_t t; CHECK(pthread_create(&t,NULL,thread_fn,NULL)==0); CHECK(pthread_join(t,NULL)==0);
 printf("thread after join: shared=%d\n",shared_value);
}
static volatile sig_atomic_t notified=0;
static void handler(int sig) { (void)sig; notified=1; }
static void signal_demo(void) {
 struct sigaction sa={0}; sa.sa_handler=handler; CHECK(sigemptyset(&sa.sa_mask)==0);
 CHECK(sigaction(SIGUSR1,&sa,NULL)==0); CHECK(raise(SIGUSR1)==0);
 printf("signal handler flag=%d\n",(int)notified);
}
static void sendfile_demo(void) {
 char path[]="./send-lab-XXXXXX",b[4]={0}; int fd=mkstemp(path); CHECK(fd>=0); CHECK(unlink(path)==0);
 CHECK(write(fd,"xyz",3)==3); int s[2]; CHECK(socketpair(AF_UNIX,SOCK_STREAM,0,s)==0);
 off_t offset=0; CHECK(sendfile(s[0],fd,&offset,3)==3); CHECK(read(s[1],b,3)==3);
 printf("sendfile: received=%s offset=%lld\n",b,(long long)offset);
 CHECK(close(s[0])==0); CHECK(close(s[1])==0); CHECK(close(fd)==0);
}
int main(void) {
 setvbuf(stdout,NULL,_IONBF,0);
 process_thread_demo(); fd_demo(); memory_demo(); epoll_demo(); signal_demo(); sendfile_demo();
 return 0;
}
