All pastes #1476815 Raw Edit

Anonymous

public c v1 · immutable
#1476815 ·published 2009-06-27 21:16 UTC
rendered paste body
// 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;}