// author OBLiQUE// copyleft 2009// daemon skeleton// p0wnbox.com// compile it with: gcc daemon.c -o daemon#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <syslog.h>#include <signal.h>#include <fcntl.h>#include <sys/un.h>#include <sys/socket.h>#include <sys/stat.h>#define MAX_STRING 0xfff#define SOCKET_PATH "/tmp/daemon_stream_socket"void black_magic(const char *daemon_name);int daemon_listen(const char *pipe_path);int daemon_accept(int listenfd, uid_t *client_uid);int daemon_conf();void open_connections();void sig_term(int signum);void sig_hup(int signum);typedef struct _client_node_ { int fd; uid_t uid; struct _client_node_ *next;} client_node;client_node *head=NULL; // head of the linked-listclient_node *tail=NULL; // tail of the linked-listclient_node *new_client_node(int fd, uid_t uid);int client_add(int fd, uid_t uid);int client_del(int fd);void clients_clear();int main(int argc, char *argv[]){ printf("welcome to daemon skeleton\n"); black_magic("daemon_skeleton"); // daemonize the process syslog(LOG_NOTICE,"daemon started successfully"); open_connections(); return EXIT_SUCCESS;}/* there is some rules to make a daemon process * 1. set file mode creation mask so you can create a files with specific permissions * 2. fork a child and kill the parrent to detach form shell * 3. create new session and become a session leader * 4. change the working directory to root directory (/) to prevent unmount failure * 5. redirect stdin,stdout and stderr to /dev/null * 6. if you want create a log file with syslog * 7. if you want set signal handlers */void black_magic(const char *daemon_name){ int i, fd0, fd1, fd2; pid_t pid; /* set file mode creation mask */ umask(0); /* this must be the compliment of chmod * example: * chmod 777 == umask(0) * if i want to calculate the chmod 640 i subtract 640 from 777 * 777 - 640 = 137 * so chmod 640 == umask(137) * * because this program it's only a skeleton and i dont care about * file permissions i use the umask(0) */ /* detach form shell */ if ((pid=fork())<0) { // fork a child and check if it's forked fprintf(stderr,"fork(): %s .. exiting\n",strerror(errno)); exit(EXIT_FAILURE); } else if (pid>0) exit(EXIT_SUCCESS); // kill the parent setsid(); // create new session and become a leader chdir("/"); // change to root directory /* redirect stdin,stdout and stderr to /dev/null */ close(0); close(1); close(2); fd0 = open("/dev/null",O_RDWR); // open /dev/null to file descriptor 0 fd1 = dup(0); // copy the file descriptor 0 to fd1 fd2 = dup(0); // copy the file descriptor 0 to fd2 openlog(daemon_name,LOG_PID,LOG_DAEMON); // initialize the log file if (fd0!=0 || fd1 !=1 || fd2!=2) { // check if the file descriptions are ok syslog(LOG_ERR,"file description proplem\n"); exit(EXIT_FAILURE); } // read the configuration file daemon_conf(); // SIGTERM assures the execution of the function sig_term in case dameon gets killed if (signal(SIGTERM,sig_term)==SIG_ERR) syslog(LOG_ERR,"signal(): cannot register SIGTERM"); // regiter SIGHUP so you can use kill -HUP daemon_pid to reload the configurations if (signal(SIGHUP,sig_hup)==SIG_ERR) syslog(LOG_ERR,"signal(): cannot register SIGHUP");}// create a domain socket and listen to itint daemon_listen(const char *socket_path){ int sockfd, err; struct sockaddr_un un; if ((sockfd=socket(AF_UNIX,SOCK_STREAM,0))<0) // open domain stream socket return -1; unlink(socket_path); // delete the file memset(&un,0,sizeof(un)); // zero the un struct un.sun_family=AF_UNIX; // assing the socket family strncpy(un.sun_path,socket_path,sizeof(un.sun_path)-1); // copy socket_path to struct if (bind(sockfd,(struct sockaddr*)&un,sizeof(struct sockaddr_un))<0) // assing address to socket goto error; if (listen(sockfd,SOMAXCONN)<0) // set socket to listening mode goto error; return sockfd; // return the file descriptorerror: // on error err=errno; // save errno close(sockfd); // close socket fd errno=err; // restore errno return -1;}// accept a client and return the uid and fd of the connectionint daemon_accept(int listenfd, uid_t *client_uid){ int clientfd, err; socklen_t slen; struct sockaddr_un un; struct stat st; slen=sizeof(un); // size of un struct if ((clientfd=accept(listenfd,(struct sockaddr*)&un,&slen))<0) // accept a connection and return the fd return -1; if (stat(un.sun_path, &st)<0) { // check if the fd exist // on error err=errno; // save errno close(clientfd); // close client fd errno=err; // restore errno return -1; } if (client_uid!=NULL) // if this function called without NULL for the uid argument *client_uid=st.st_uid; // assing file uid to client_uid return clientfd; // return client file descriptor}/* the implementation (including all relevant functions/structures) * of data structure linked-list will remain uncommented *//* start of linked-list functions */// create new client_node structclient_node *new_client_node(int fd, uid_t uid){ client_node *tmp = (client_node*)malloc(sizeof(client_node)); tmp->fd=fd; tmp->uid=uid; tmp->next=NULL; return tmp;}// add the client to the linked-listint client_add(int fd, uid_t uid){ client_node *new_client = new_client_node(fd,uid); if (new_client==NULL) { syslog(LOG_ERR,"memory overflow. can't create new connection\n"); return 0; } if (head==NULL) head=tail=new_client; else { tail->next=new_client; tail=tail->next; } return 1;}// delete the client form the linked-listint client_del(int fd){ client_node *p=head, *n; if (p==NULL) return 1; if (p->fd==fd) { if (head==tail) tail=NULL; head=head->next; free(p); return 1; } n=p->next; while (n!=NULL) { if (n->fd==fd) { p->next=n->next; free(n); if (p->next==NULL) tail=p; return 1; } p=p->next; n=n->next; } return 0;}// clear the linked-listvoid clients_clear(){ client_node *tmp; while (head!=NULL) { tmp=head; head=head->next; close(tmp->fd); // close file descriptor free(tmp); } tail=NULL;}/* end of linked-list functions */// if you kill the process this function will be calledvoid sig_term(int signum){ syslog(LOG_NOTICE,"SIGTERM received .. clear clients"); clients_clear(); syslog(LOG_NOTICE,"exiting\n"); exit(EXIT_SUCCESS);}// load configuretation fileint daemon_conf(){ syslog(LOG_NOTICE,"reading the configuration file"); // here you can load your configuration file syslog(LOG_NOTICE,"finished");}// reaload configuration file if SIGHUP receivedvoid sig_hup(int signum){ syslog(LOG_NOTICE,"SIGHUP received .. execute daemon_conf()"); daemon_conf();}void open_connections(){ int listenfd, clientfd, maxfd, recvlen, cmdnum; char buf[MAX_STRING]; uid_t uid; fd_set tmp_set, master_set; client_node *node_ptr; if ((listenfd=daemon_listen(SOCKET_PATH))<0) { // put daemon to listening mode andreceive the socket fd syslog(LOG_ERR,"daemon_listen(): %m .. exiting"); exit(EXIT_FAILURE); } FD_ZERO(&master_set); // clear the set FD_SET(listenfd,&master_set); // add listenfd maxfd=listenfd; // maximum descriptor number while (1) { // i use tmp_set for the select function and master_fd for adding new fd tmp_set=master_set; if (select(maxfd+1,&tmp_set,NULL,NULL,NULL)<0) syslog(LOG_ERR,"select(): %m"); if (FD_ISSET(listenfd,&tmp_set)) { // if listenfd is ready if ((clientfd=daemon_accept(listenfd,&uid))<0) // accept the new connection and receive the client file descriptor syslog(LOG_ERR,"daemon_accept(): %m"); client_add(clientfd,uid); // add the fd an uid to the list FD_SET(clientfd,&master_set); // add the fd to the set if (clientfd>maxfd) maxfd=clientfd; syslog(LOG_NOTICE,"new connection: uid=%d, fd=%d",uid,clientfd); continue; } node_ptr=head; while (node_ptr!=NULL) { clientfd=node_ptr->fd; if (FD_ISSET(clientfd,&tmp_set)) { // if clientfd is ready recvlen=recv(clientfd,buf,MAX_STRING,0); // read from socket if (recvlen<0) { // on error syslog(LOG_ERR,"read(): %m"); } else if (recvlen==0) { // if EOF syslog(LOG_NOTICE,"close connection: uid=%d, fd=%d",uid,clientfd); FD_CLR(clientfd,&master_set); client_del(clientfd); close(clientfd); if (head==NULL) maxfd=listenfd; } else { // if its ok cmdnum=daemon_commands(buf,node_ptr); // send the buf to daemon_command switch (cmdnum) { case 1: // exit syslog(LOG_NOTICE,"close connection: uid=%d, fd=%d",uid,clientfd); FD_CLR(clientfd,&master_set); client_del(clientfd); close(clientfd); break; case 2: // shutdown clients_clear(); syslog(LOG_NOTICE,"exiting"); exit(EXIT_SUCCESS); break; } } } node_ptr=node_ptr->next; } // end of while (node_ptr!=NULL) } // end of while (1)}// and finally the function that you can manipulate the messagesint daemon_commands(const char *buf, const client_node *node){ syslog(LOG_NOTICE,"message form uid %d fd %d: %s",node->uid,node->fd,buf); if (!strcmp("exit",buf)) return 1; else if (!strcmp("shutdown",buf)) return 2; else if (!strcmp("version",buf)) { char v[] = "daemon skeleton v0.1"; send(node->fd,v,strlen(v)+1,0); } else { char replay[] = "message received"; send(node->fd,replay,strlen(replay)+1,0); } return 0;}