All pastes #993035 Raw Edit

Stuff

public text v1 · immutable
#993035 ·published 2008-04-22 09:22 UTC
rendered paste body
/*
 * fwld - firmware loading daemon: catches firmware request events from the 
 * kernel and serves them.
 * 
 *
 *	Only kernels from version 2.6.10* on provide the uevent netlink socket, 
 * needed by this program.
 * 
 * Copyright (C) 2007 Mr. KIKKO (mrkiko.rs@gmail.com)
 
 * This program is an HEAVILY modified version of a sample event listener. Here 
 * are the copyrights:
 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
 *
 *	This program is free software; you can redistribute it and/or modify it
 *	under the terms of the GNU General Public License as published by the
 *	Free Software Foundation version 2 of the License.
 *
 *	This program is distributed in the hope that it will be useful, but
 *	WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License along
 *	with this program; if not, write to the Free Software Foundation, Inc.,
 *	675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/user.h>
#include <asm/types.h>
#include <linux/netlink.h>

/* environment buffer, the kernel's size in lib/kobject_uevent.c should fit in */
#define HOTPLUG_BUFFER_SIZE		1024
#define HOTPLUG_NUM_ENVP		32
#define OBJECT_SIZE			512

/* Some variables that should be accessed within other functions */
int sock;                /* socket FD                   */
char *envp[HOTPLUG_NUM_ENVP];

/* Function prototypes... */
void handle_exit(int e);
int handle_fwrequest(char *scpname);


int main(int argc, char *argv[])
{
	/* Variables needed during the entire program... */
	struct sockaddr_nl snl;  /* netlink socket */
	/*
	  Here we use a sockadr_nl structure since we want to use it to create a 
	  netlink socket.
	*/
	int retval;              /* generic return value holder */
                           /* buffer to hold environment entries passed to the
                            script invoked by this program... */
		                                 /* environment passed by module       */
  static char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];
	static char object[OBJECT_SIZE]; /* char array to contain an object    */
	//const char *devpath;             /* pointer to contain the device path */
	const char *action;              /* action requested by module         */
	int i;
	char *pos;                       /* used to access these arrays        */
	size_t bufpos;                   
	ssize_t buflen;                  /* used to hold the message length    */



	/*
	  Exit if not running as root!
	*/
	if (getuid() != 0) {
		printf("%s(%d): this program needs to run under root privileges!\n",argv[0],getpid());
		exit(1);
	}
	
	if (argc != 2){
	  printf("%s(%d): this program needs as it's only argument the complete LF script path!\n",argv[0],getpid());
	  exit(3);
	}

	/*
	  Fill the snl structure with 0x0 characters.
	*/
	memset(&snl, 0x00, sizeof(struct sockaddr_nl));
	

	/*
	  snl initialization: here we define the kind of socket we need.
	*/
	
	/*
	  We want to use this socket for netlink communication, so we should specify the right family.
	  Netlink  is  used  to transfer information between kernel and userspace 
	  processes. It consists  of  a  standard  sockets-based interface  for 
	  userspace processes and an internal kernel API for kernel modules.
	  
	  We need to catch firmware request events from there.
	*/
	snl.nl_family = AF_NETLINK;
	/*
	  A socket should have a unique value in this space.
	  The pid, they say, doesn't identify a process necessarily.
	  Since this is the first netlink socket, the instruction:
	  snl.nl_pid = 0;
	  is equal to the one we used.
	*/
	snl.nl_pid = getpid();
	/*
	  Multicast group mask... ?????????
	  Here we need:
	  - the CAP_NET_ADMIN
	  or
	  - an EUID of 0
	  
	  We want to listen to the multicast group 1 (1 is a bitmask)
	*/
	snl.nl_groups = 1;
	
	/* logging: here logging starts... */
	openlog(argv[0], LOG_CONS | LOG_PID, LOG_DAEMON);
	perror("Log init: "); /* mhmmm.... */	

	/*
	  Try to create the socket:
	  Socket domain: PF_NETLINK;
	  Socked type: SOCK_DGRAM (equivalent to a raw socket in this case)
	  Socked protocol: NETLINK_KOBJECT_UEVENT
	*/
	sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
	
	/* Something wrong happened: we can't open the socket, so we exit */
	if (sock == -1) {
		syslog(LOG_ERR, "error getting socket, exiting...\n");
		exit(1);
	}

	/* Assign a name to a socket: may be I'll understand it better later... */
	retval = bind(sock, (struct sockaddr *) &snl,
		      sizeof(struct sockaddr_nl));
	/*
	  Something wrong happened: we should close the socket and exit.
	  The -goto- usage will be removed here soon!
	*/
	if (retval < 0) {
		syslog(LOG_ERR,"socket bind failed, exiting...\n");
		handle_exit(19);
	}
  
  /*
    So now we can go to background!
  */
  if (daemon(1,1) != 0) {
    syslog(LOG_ERR, "can't become a daemon!");
    handle_exit(10);
  }
  
  /*
    How can I set a nice process title here? setproctitle? mhm... 
  */
  //setproctitle("FWLD - CATCHING EVENTS!");
	
	syslog(LOG_WARNING, "successfully started and waiting for firmware requests!");
	while (1) {
		/* OH... FINALLY... we can wait for messages to come in! */
		buflen = recv(sock, &buffer, sizeof(buffer), 0);
		/* What's happened? */
		if (buflen <  0) {                      /* OOPS!              */
			syslog(LOG_ERR, "error receiving message");
			continue;
		}

		if ((size_t)buflen > sizeof(buffer)-1)   /* Message too long!   */
		{
			buflen = sizeof(buffer)-1; /* make buflen coherent... */
		  buffer[buflen] = '\0';       /* 0-terminate the string by hand! */
		  syslog(LOG_ERR, "too long message. Problems with your kernel?");
    }
		/* save start of payload */
		bufpos = strlen(buffer) + 1;

		action = buffer;
		pos = strchr(buffer, '@');
		if (!pos)
			continue;
		pos[0] = '\0';

		//devpath = &pos[1];

		/* hotplug events have the environment attached - reconstruct envp[] */
		for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
			int keylen;
			char *key;
			key = &buffer[bufpos];
			keylen = strlen(key);
			envp[i] = key;
			bufpos += keylen + 1;
		}
		envp[i] = NULL;

		/*
		  Is the current an "add" action?
		  If not, we should stop processing JUST NOW!
		*/
		if (strcmp(action, "add") == 0) /* Ok, we should continue! */
		  for (i = 0; envp[i] != NULL; i++){
			  if (strncmp("FIRMWARE", envp[i], 8) == 0) {
			    /*
			      Here we should handle the firmware request we had.
			      So, we call the handle_fwrequest function, which does all the fork 
			      paraphernalia.
			    */
			    syslog(LOG_WARNING, "a request has been catched!");
			    retval = handle_fwrequest(argv[1]);
			    if (retval != 0) {
			      /*
			        Errors occured during the event handling process.
			      */
			      syslog(LOG_ERR, "event handler exited with error code %d!",retval);
			      handle_exit(7);
			    }
			  }
			}
	  }
}

int handle_fwrequest(char *scpname){
  pid_t p;     /* process ID of this event handler */
  int retmain; /* integer returned to main */
  
  retmain = 0; /* if no errors occurs, all is ok */
  p = fork();
  switch(p){
    case 0: /* this case is seen by the CHILD! */
	  {
      if (execve(scpname, NULL, envp) == -1){
        /*
          On success execve never returns, but we know it'll not be always a
          success.
          Here we use a rudimentary, but simple and quite extensible, method
          to warn the master process some errors happened.
        */
        exit(16);
      }
      break; /* BUT WE SHOULD NEVER GET THERE */
		}
	  default: /* master... */
		{
      /*
			  Some considerations needed here.
			  - our child is not our anymore: it's image is replaced by 
			  execve. This way, we just can't know if the child is going to 
			  change (or not) it's status.
			  - may be the child will create other childs: we here launch a 
			  script which probably will not generate other childs, but 
			  aniway we need a consistent way to wait for it.
			  For this we use the waitpid system call and not the wait() 
			  one as probably suggested by many.
			  If this is wrong, contact me...
			*/
			int status;
      do {
			  waitpid(p, &status, 0);
			} while(!(WIFEXITED(status) || WIFSIGNALED(status)));
			
			/*
			  Failures in the event handler or in the external command launched by 
			  it?
			*/
			if (WEXITSTATUS(status) != 0) {
			  retmain = status;
			}
	  } /* end of the default case instructions set  */
	} /* end of the switching mechanism instructions set */
	/* all was ok... */
	return retmain;
} /* end of the handle_fwrequest function */

void handle_exit(int e){
	syslog(LOG_ERR, "handling program termination: closing socket...");
	close(sock);
	exit(e);
}