(file) Return to fusdd.c CVS log (file) Jump to this file's LXR Page (dir) Up to [CENS] / emstar / fusd / fusdd

File: [CENS] / emstar / fusd / fusdd / fusdd.c (download) / (as text)
Revision: 1.20, Fri Feb 25 17:51:51 2005 UTC (4 years, 8 months ago) by girod
Branch: MAIN
CVS Tags: rdd_alpha_version_1, pregeonet, acoustic-05-18-06, PRE_TOSNIC_FIX, PRE_64BIT, MOTENIC_PRE_BUGFIX_20050415, LAURA_CALIBRATION_EXPERIMENTS, HEAD, ESS_RELEASE_3_5, ESS_RELEASE_3_4, ESS_RELEASE_3_3, ESS_RELEASE_3_2, ESS_RELEASE_3_1, ESS_RELEASE_3_0, ESS_RELEASE_2_0, ESS_CONNECTIVITY, ESS_CENTROUTE_TESTING, ESS2-CMS-V1_5_pretest, ESS2-CMS-V1_4cMergeSympathy_2, ESS2-CMS-V1_4c, ESS2-CMS-V1_4b, ESS2-CMS-V1_4a, ESS2-CMS-V1_3, ESS2-CMS-V1_2, ESS2-CMS-V1_1, ESS2-CMS-V1_0, EMSTAR_RELEASE_2_5, EMSTAR_RELEASE_2_1_BRANCH, EMSTAR_RELEASE_2_1, CYCLOPS_RELEASE_CANDIDATE_2_0, CYCLOPS_PRERELEASE_STABLE, CENTROUTE_EMSTAR_SOCKETS, BG_1_0, BANGLADESH_ARSENIC_1_2, BANGLADESH_ARSENIC_1_1, AMARSS_JR_DEPLOYMENT_6_05_07
Changes since 1.19: +18 -15 lines
revised previous logging fixes.  now, if a process uses -o to set
loglevel locally, that info is correctly propagated back to emrun as a
custom
loglevel.

/*
 *
 * Copyright (c) 2003 The Regents of the University of California.  All 
 * rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Neither the name of the University nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 
/*
 *  FUSDd
 *
 *  The user-space component of FUSD for non-DevFS Linux 2.4 systems
 *
 */

#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>  
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>

#include "fusd.h"
#include "fusdd_i.h"
#include <fusd_msg.h>

#define FUSD_MAX_DEVS  20000
#define F_PATH_MAX       (FUSD_MAX_NAME_LENGTH + 10)

extern int unlink_dirs;

struct dev {
  char *name;
  uint16_t major;  
  uint16_t minor;
  int mark:1;
  int del:1;
};

struct dev *dev_lookup(char *name);
void dev_insert(struct dev *add);
void dev_compress();
void dev_dump();

/* current active devices */
struct dev *active_devs=NULL;
int dev_count=0;
int dev_alloc=0;
int verbose=0;

#define DEV_TO_NUM(x) (makedev((x)->major, (x)->minor))
#define DEV_EQ(x,y) (((x)->major == (y)->major) && ((x)->minor == (y)->minor))
#define DEV_ZERO(x) (((x)->major == 0) && ((x)->minor == 0))

/*
 *  debug function
 */

void dev_dump()
{
  int i;
  printf("%d devs, %d alloc\n", dev_count, dev_alloc);
  for (i=0; i<dev_count; i++) {
    printf("%s %d\n", active_devs[i].name, active_devs[i].del);
  }
  printf("\n");
}


void dev_test()
{
  struct dev d = {};

#define TESTI(s)  d.name = strdup(s); dev_insert(&d); dev_dump();
#define TESTD(s)  dev_lookup(s)->del = 1; dev_dump();

  TESTI("b");
  TESTI("d");
  TESTI("f");
  TESTI("a");
  TESTI("aa");
  TESTI("g");

  TESTD("d");
  TESTD("a");
  TESTD("g");
  dev_compress(); dev_dump();

  TESTD("f");
  dev_compress(); dev_dump();
}

/*
 *  device list manipulation
 */

int dev_cmp(const void *x, const void *y)
{
  struct dev *xd = (struct dev *)x;
  struct dev *yd = (struct dev *)y;
  return strcmp(xd->name, yd->name);
}

struct dev *dev_lookup(char *name)
{
  struct dev key = {
    name: name
  };
  if (active_devs == NULL) return NULL;
  return bsearch(&key, active_devs, dev_count, sizeof(struct dev), dev_cmp);
}

void dev_insert(struct dev *add)
{
  int i;

  dev_count++;

  /* need to expand? */
  if (dev_alloc < dev_count) {
    dev_alloc *= 2;
    if (dev_alloc == 0) dev_alloc = 64;
    active_devs = realloc(active_devs, sizeof(struct dev)*dev_alloc);
  }

  /* locate position */
  for (i=dev_count-1; i>0; i--) {
    if (strcmp(add->name, active_devs[i-1].name) > 0) { 
      goto found;
    }
    active_devs[i] = active_devs[i-1];
  }
  
 found:
  /* store */
  active_devs[i] = *add;
}


void dev_compress()
{
  int i;
  int shift=0;

  for (i=0; i<dev_count; i++) {
    if (active_devs[i].del) {
      free(active_devs[i].name);
      shift++;
      continue;
    }
    if (shift) active_devs[i-shift] = active_devs[i];
  }
  dev_count -= shift;
}


/*
 *  Prepend /dev.. careful.. static allocation!
 */

char *dev_path(char *name)
{
  static char path[F_PATH_MAX+1];

  /* prepend /dev */
  strcpy(path, DEFAULT_DEV_ROOT);
  strcat(path, name);

  return path;
}


/*
 * mkdir_with_parents: given a pathname, create a directory with that
 * name and possibly any parents of that directory, if necessary.
 *
 * Returns:
 *   0 if the directory was created successfully (or already existed)
 *  -1 with errno set appropriately if there was an error creating the
 *     directory or any of its parents
 */

int f_mkdir_with_parents(char *path_orig)
{
  char path[F_PATH_MAX+1];
  char *parent_end;
  
  strcpy(path, path_orig);

  /* skip over any leading slashes, then find the first slash after
   * that */
  parent_end = index(path + strspn(path, "/"), '/');

  /* now, for each component of the path, create the dir */
  while (parent_end != NULL) {
    *parent_end = '\0';

    if (mkdir(path, 0755) < 0 && errno != EEXIST) {
      return -1;
    }

    *parent_end = '/';
    parent_end = index(parent_end+1, '/');
  }

  return 0;
}



int clear_marks(void *data) 
{
  int i;
  if (verbose) fprintf(stderr, "Clearing Marks\n");
  /* clear the marks first */
  for (i=0; i<dev_count; i++)
    active_devs[i].mark=0;
  return 0;
}


void dev_remove(struct dev *dev)
{
  if (dev->name) {
    if (verbose)
      fprintf(stderr, "Unlinking unused device %s\n", dev->name);
    unlink(dev_path(dev->name));
    
    if (unlink_dirs) {
      /* rmdir the enclosing directory */
      char *tmp = strdup(dev->name);
      char *slash;
      
      while ((slash = strrchr(tmp, '/'))) {
	*slash = 0;
	
	if (verbose) 
	  fprintf(stderr, "Removing dir %s\n", dev_path(tmp));
	
	if (rmdir(dev_path(tmp)) < 0) {
	  if (verbose) 
	    fprintf(stderr, "Unable to remove dir %s: %m\n", tmp);
	  break;
	}
      }
      
      free(tmp);
    }
  }
  dev->del = 1;
}


int unlink_unmarked(void *data)
{
  int i;
  if (verbose) fprintf(stderr, "Unlinking unmarked\n");
  /* unlink any unmarked devices */
  for (i=0; i<dev_count; i++) {
    if (active_devs[i].mark == 0)
      dev_remove(&active_devs[i]);
  }
  dev_compress();
  return 0;
}


int process(fusd_status_t *fs, int count, void *data)
{
  int i;

  for (i=0; i<count; i++, fs++) {
    struct stat s;
    int fd;
    struct dev *ptr = dev_lookup(fs->name);
    char *path;

    /* if zombie, remove if present */
    if (fs->zombie) {
      if (verbose)
	fprintf(stderr, "ignoring zombie %s\n", fs->name);
      continue;
    }
  
    /* if null device, remove if present */
    if (DEV_ZERO(fs)) {
      if (verbose)
	fprintf(stderr, "ignoring non-device %s\n", fs->name);
      continue;
    }

    /* if present, verify the device setting */
    if (ptr) {
      /* we're done if it matches out current state */
      if (DEV_EQ(ptr,fs))
	goto mark;
      /* otherwise it will be updated after we succeed */
    }

    /* 
     * OK, now we have an entry that might need to be added
     */

    path = dev_path(fs->name);
    
    if (verbose)
      fprintf(stderr, "checking '%s' -> %d,%d [%x]\n",
	      path, fs->major, fs->minor, (uint32_t)DEV_TO_NUM(fs));
    
    /* Stat the device file. */
    if (stat(path, &s) < 0) {
      if (errno == ENOENT) {
	
	/* create any subdirectories */
	if (f_mkdir_with_parents(path) < 0) {
	  fprintf(stderr, "Unable to create directory components for device %s\n", path);
	  continue;
	}
	
	/* do the mknod */
	goto create;
      }
      
      fprintf(stderr, "Stat of %s failed: %m\n", path);
      continue;
    }
    
    /* Check the stat, see if it matches */
    
    /* if this is a regular file, delete it! */
    if (S_ISREG(s.st_mode)) {
      fprintf(stderr, "Deleting existing regular file: %s\n", path);
      if (unlink(path) < 0) {
	fprintf(stderr, "Unable to unlink existing regular file %s\n", path);
	goto remove;
      }
      goto create;
    }
    
    /* if this is not a chrdev, ignore it -- (this may hang the client) 
     * this might be a directory!!
     *  $$$ figure out a better thing to do here?? */
    if (!S_ISCHR(s.st_mode)) {
      
      if (S_ISDIR(s.st_mode)) 
	fprintf(stderr, "Device overloading a directory!! NOT deleting: %s\n", path);
      else
	fprintf(stderr, "Device overloading a non-regular file.. NOT deleting: %s\n", path);
      
      /* don't goto remove here because we don't want to delete this file! */
      if (ptr) ptr->del = 1;
      continue;
    }

    /* otherwise, if it's a chrdev, check the bits */
    if (s.st_rdev != DEV_TO_NUM(fs)) {
      /* delete it if they don't match */
      if (unlink(path) < 0) {
	fprintf(stderr, "Unable to unlink existing device file %s\n", path);
	goto remove;
      }
    }
    
    /* the existing inode is correct. */
    else {
      /* if it's marked unmade then do the open to set it 'made' */
      if (fs->unmade)
	goto do_open;
      
      /* otherwise just mark it as OK */
      goto mark;
    }
    
  create:
    /* OK, now do the mknod! */
    if (mknod(path, S_IFCHR | 0777, DEV_TO_NUM(fs)) < 0) {
      fprintf(stderr, " *** Unable to mknod device %d.%d to %s\n", 
	      fs->major, fs->minor, path);
      goto remove;
    }
    
    if (verbose)
      fprintf(stderr, " *** mknod'd device %d,%d (%x) dev %s\n", 
	      fs->major, fs->minor, (uint32_t)DEV_TO_NUM(fs), path);
    
  do_open:
    /* open it to activate it! */
    fd = open(path, O_RDONLY);
    if (fd >= 0) {
      fprintf(stderr, " *** What?? our open succeeded??\n");
      close(fd);
    }
    
    else {
      switch (errno) {

      case ENOENT:
	/* this happens if the device has already been deleted in the kernel */
	/* if ptr is valid, we just don't mark it, it will be removed later  */
	if (ptr == NULL)
	  unlink(path);
	goto remove;

      case EXDEV:
	/* this means we succeeded */
	if (verbose)
	  fprintf(stderr, " *** Open should have 'made' dev %s\n", path);
	break;

      default:
	fprintf(stderr, " *** Oops.. unexpected error opening dev %s: %m\n", path);
      }
    }
    
  mark:
    if (ptr) {
      /* if key is already there, update the state */
      ptr->major = fs->major;
      ptr->minor = fs->minor;
      ptr->mark = 1;	
    }
      
    else {
      struct dev key = {
	name: strdup(fs->name),
	major: fs->major,
	minor: fs->minor,
	mark: 1
      };

      dev_insert(&key);
    }
    continue;

  remove:
    if (ptr) dev_remove(ptr);
  }

  /* finalize deletions */
  dev_compress();

  return STATUS_OK;
}


void start_fusdd(int set_verbose)
{
  int fd;
  int status;
  verbose = set_verbose;

  fprintf(stderr, "fusdd: FUSDd Starting...%s\n", verbose ? " (Verbose Mode)" : "");
  umask(0);
  
  /* check UID */
  if (geteuid()) {
    fprintf(stderr, "fusdd: Sorry, I must run as root!\n");
    exit(1);
  }

  /* modprobe kfusd */
  status = system("/sbin/modprobe kfusd");
  if (status < 0) {
    fprintf(stderr, "fusdd: modprobe failed: %m\n");
  }
  else if (status) {
    fprintf(stderr, "fusdd: modprobe returned '%d', can't find fusd?  Continuing...\n", 
	    status);
  }

  /* mknod the FUSD control channel etc */
  f_mkdir_with_parents(FUSD_CONTROL_DEVNAME);
  if (mknod(FUSD_CONTROL_DEVNAME, S_IFCHR | 0777, 
	    makedev(FUSD_DEV_MAJOR, FUSD_CONTROL_MINOR)) < 0)
    if (errno != EEXIST)
      fprintf(stderr, "Unable to mknod FUSD control device: %m\n");
  if (mknod(FUSD_STATUS_DEVNAME, S_IFCHR | 0777, 
	    makedev(FUSD_DEV_MAJOR, FUSD_STATUS_MINOR)) < 0)
    if (errno != EEXIST)
      fprintf(stderr, "Unable to mknod FUSD status device: %m\n");
  
  /* open the FUSD status device */
  fd = open(FUSD_STATUS_DEVNAME, O_RDONLY);
  if (fd < 0) {
    fprintf(stderr, "fusdd: Can't open %s.. Make sure FUSD module is inserted: %m\n", 
	    FUSD_STATUS_DEVNAME);
    goto delete;
  }

  /* turn on binary mode */
  if (ioctl(fd, FUSD_STATUS_USE_BINARY, NULL) < 0) {
    fprintf(stderr, "fusd: BINARY_MODE ioctl() failed: %m\n");
    exit(1);    
  }
  
  /* daemonize? */
  if (!verbose) {
    int status = fork();

    if (status < 0) {
      fprintf(stderr, "fusdd: Unable to fork!\n");
      exit(1);
    }

    if (status)
      return;

    /* child: daemonize */
    setpgrp();
    chdir("/");
    close(0);
    close(1);
  }

  /* Tell FUSD who we are */
  if (ioctl(fd, FUSD_STATUS_I_AM_FUSDD, NULL) < 0) {
    if (errno == 524) {
      fprintf(stderr, "fusdd: This FUSD uses DevFS: daemon exiting.\n");
      return;
    }
    fprintf(stderr, "fusd: I_AM_FUSDD ioctl() failed: %m\n");
    goto delete;
  }

  if (!verbose)
    close(2);

  {
    fusd_status_context_t fsctx = {
      fd: fd,
      new_data_cb: process,
      begin_full_cb: clear_marks,
      end_full_cb: unlink_unmarked,
    };

    /* Now we read! */
    while (1) {
      int status;

      /* process it */
      status = fusd_status_process(&fsctx);

      switch (status) {
      case STATUS_ERR:
	fprintf(stderr, "fusd: Read failed: %m\n");
	exit(1);
      case STATUS_DONE:
	if (fusd_status_wait(&fsctx) < 0) {
	  fprintf(stderr, "fusd: Poll failed: %m\n");
	  exit(1);
	}
      }
    }
  }

 delete:
  unlink(FUSD_CONTROL_DEVNAME);
  unlink(FUSD_STATUS_DEVNAME);
  exit(1);
}

CENS CVS Mailing List
Powered by
ViewCVS 0.9.2