Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

proc.c

/*
 * ipl_tools - Linux for System z reipl and shutdown tools
 *
 * Scanner for /proc files
 *
 * Copyright IBM Corp. 2001, 2010
 * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
 *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
 */

#include "proc.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include "ipl_tools.h"

static const char proc_part_filename[] = "/proc/partitions";
static const char proc_dev_filename[] = "/proc/devices";

00025 struct file_buffer {
      char* buffer;
      off_t pos;
      size_t length;
};

#define INITIAL_FILE_BUFFER_SIZE    1024

/* Read file into buffer without querying its size (necessary for reading files
 * from /proc or /sys). Upon success, return zero and set BUFFER to point to
 * the file buffer and SIZE (if non-null) to contain the file size. Return
 * non-zero otherwise. Add a null-byte at the end of the buffer if
 * NIL_TERMINATE is non-zero.  */
int
misc_read_special_file(const char* filename, char** buffer, size_t* size,
                   int nil_terminate)
{
      FILE* file;
      char* data;
      char* new_data;
      size_t count;
      size_t current_size;
      int current;

      file = fopen(filename, "r");
      if (file == NULL)
            ERR_EXIT_ERRNO("Could not open \"%s\"", filename);
      current_size = INITIAL_FILE_BUFFER_SIZE;
      count = 0;
      data = (char *) malloc(current_size);
      if (data == NULL) {
            fclose(file);
            return -1;
      }
      current = fgetc(file);
      while (current != EOF || nil_terminate) {
            if (current == EOF) {
                  current = 0;
                  nil_terminate = 0;
            }
            data[count++] = (char) current;
            if (count >= current_size) {
                  new_data = (char *) malloc(current_size * 2);
                  if (new_data == NULL) {
                        free(data);
                        fclose(file);
                        return -1;
                  }
                  memcpy(new_data, data, current_size);
                  free(data);
                  data = new_data;
                  current_size *= 2;
            }
            current = fgetc(file);
      }
      fclose(file);
      *buffer = data;
      if (size)
            *size = count;
      return 0;
}

/* Get the contents of a file and fill in the respective fields of
 * FILE. Return 0 on success, non-zero otherwise. */
static int
get_file_buffer(struct file_buffer* file, const char *filename)
{
      int rc;

      rc = misc_read_special_file(filename, &file->buffer, &file->length, 0);
      file->pos = 0;
      return rc;
}


/* Free resources allocated for file buffer identified by
 * FILE. */
static void
free_file_buffer(struct file_buffer* file)
{
      if (file->buffer != NULL) {
            free(file->buffer);
            file->buffer = NULL;
            file->pos = 0;
            file->length = 0;
      }
}


/* Return character at current FILE buffer position or EOF if at end of
 * file. */
static int
current_char(struct file_buffer* file)
{
      if (file->buffer != NULL)
            if (file->pos < (off_t) file->length)
                  return file->buffer[file->pos];
      return EOF;
}


/* Advance the current file pointer of file buffer FILE until the current
 * character is no longer a whitespace or until the end of line or file is
 * reached. Return 0 if at least one whitespace character was encountered,
 * non-zero otherwise. */
static int
skip_whitespaces(struct file_buffer* file)
{
      int rc;

      rc = -1;
      while ((current_char(file) != '\n') && isspace(current_char(file))) {
            rc = 0;
            file->pos++;
      }
      return rc;
}


/* Scan a positive integer number at the current position of file buffer FILE
 * and advance the position respectively. Upon success, return zero and set
 * NUMBER to contain the scanned number. Return non-zero otherwise. */
static int
scan_number(struct file_buffer* file, size_t* number)
{
      int rc;
      size_t old_number;

      *number=0;
      rc = -1;
      while (isdigit(current_char(file))) {
            rc = 0;
            old_number = *number;
            *number = *number * 10 + current_char(file) - '0';
            /* Check for overflow */
            if (old_number > *number) {
                  rc = -1;
                  break;
            }
            file->pos++;
      }
      return rc;
}


/* Scan a device node name at the current position of file buffer FILE and
 * advance the position respectively. Upon success, return zero and set
 * NAME to contain a copy of the scanned name. Return non-zero otherwise. */
static int
scan_name(struct file_buffer* file, char** name)
{
      off_t start_pos;

      start_pos = file->pos;
      while (!isspace(current_char(file)) &&
             (current_char(file) != EOF))
                  file->pos++;
      if (file->pos > start_pos) {
            *name = (char *) malloc(file->pos - start_pos + 1);
            if (*name == NULL)
                  return -1;
            memcpy((void *) *name, (void *) &file->buffer[start_pos],
                   file->pos - start_pos);
            (*name)[file->pos - start_pos] = 0;
            return 0;
      } else
            return -1;
}

/* Scan for the specified STRING at the current position of file buffer FILE
 * and advance the position respectively. Upon success, return zero. Return
 * non-zero otherwise. */
static int
scan_string(struct file_buffer* file, const char *string)
{
      int i;

      for (i=0; string[i] && (current_char(file) == string[i]);
           i++, file->pos++);
      if (string[i] == '\0')
            return 0;
      return -1;
}


/* Advance the current file position to beginning of next line in file buffer
 * FILE or to end of file. */
static void
skip_line(struct file_buffer* file)
{
      while ((current_char(file) != '\n') && (current_char(file) != EOF))
            file->pos++;
      if (current_char(file) == '\n')
            file->pos++;
}


/* Return non-zero if the current file position of file buffer FILE is at the
 * end of file. Return zero otherwise. */
static int
eof(struct file_buffer* file)
{
      return file->pos >= (off_t) file->length;
}


/* Scan a line of the specified /proc/partitions FILE buffer and advance the
 * current file position pointer respectively. If the current line matches
 * the correct pattern, fill in the corresponding data into ENTRY and return 0.
 * Return non-zero otherwise. */
static int
scan_part_entry(struct file_buffer* file, struct proc_part_entry* entry)
{
      int rc;
      size_t dev_major;
      size_t dev_minor;
      size_t blockcount;
      char* name;

      /* Scan for: (\s*)(\d+)(\s+)(\d+)(\s+)(\d+)(\s+)(\S+)(\.*)$ */
      skip_whitespaces(file);
      rc = scan_number(file, &dev_major);
      if (rc)
            return rc;
      rc = skip_whitespaces(file);
      if (rc)
            return rc;
      rc = scan_number(file, &dev_minor);
      if (rc)
            return rc;
      rc = skip_whitespaces(file);
      if (rc)
            return rc;
      rc = scan_number(file, &blockcount);
      if (rc)
            return rc;
      rc = skip_whitespaces(file);
      if (rc)
            return rc;
      rc = scan_name(file, &name);
      if (rc)
            return rc;
      skip_line(file);
      entry->device = makedev(dev_major, dev_minor);
      entry->blockcount = blockcount;
      entry->name = name;
      return 0;
}


/* Release resources associated with ENTRY. */
void
proc_part_free_entry(struct proc_part_entry* entry)
{
      if (entry->name != NULL) {
            free(entry->name);
            entry->name = NULL;
      }
}

/* Scan a line of the specified /proc/devices FILE buffer and advance the
 * current file position pointer respectively. If the current line matches
 * the correct pattern, fill in the corresponding data into ENTRY and return 0.
 * Return non-zero otherwise. */
static int
scan_dev_entry(struct file_buffer* file, struct proc_dev_entry* entry,
             int blockdev)
{
      int rc;
      size_t dev_major;
      char* name;

      /* Scan for: (\s*)(\d+)(\s+)(\S+)(\.*)$ */
      skip_whitespaces(file);
      rc = scan_number(file, &dev_major);
      if (rc)
            return rc;
      rc = skip_whitespaces(file);
      if (rc)
            return rc;
      rc = scan_name(file, &name);
      if (rc)
            return rc;
      skip_line(file);
      entry->device = makedev(dev_major, 0);
      entry->name = name;
      entry->blockdev = blockdev;
      return 0;
}


/* Release resources associated with ENTRY. */
void
proc_dev_free_entry(struct proc_dev_entry* entry)
{
      if (entry->name != NULL) {
            free(entry->name);
            entry->name = NULL;
      }
}


/* Scan /proc/partitions for an entry matching DEVICE. When there is a match,
 * store entry data in ENTRY and return 0. Return non-zero otherwise. */
int
proc_part_get_entry(dev_t device, struct proc_part_entry* entry)
{
      struct file_buffer file;
      int rc;

      rc = get_file_buffer(&file, proc_part_filename);
      if (rc)
            return rc;
      rc = -1;
      while (!eof(&file)) {
            if (scan_part_entry(&file, entry) == 0) {
                  if (entry->device == device) {
                        rc = 0;
                        break;
                  }
                  proc_part_free_entry(entry);
            } else
                  skip_line(&file);
      }
      free_file_buffer(&file);
      return rc;
}

/* Scan /proc/devices for a blockdevice (BLOCKDEV is 1) or a character
 * device (BLOCKDEV is 0) with a major number matching the major number of DEV.
 * When there is a match, store entry data in ENTRY and return 0. Return
 * non-zero otherwise. */
int
proc_dev_get_entry(dev_t device, int blockdev, struct proc_dev_entry* entry)
{
      struct file_buffer file;
      int rc;
      int scan_blockdev = 0;

      rc = get_file_buffer(&file, proc_dev_filename);
      if (rc)
            return rc;
      rc = -1;
      while (!eof(&file)) {
            if (scan_string(&file, "Block") == 0) {
                  skip_line(&file);
                  scan_blockdev = 1;
                  continue;
            } else if (scan_dev_entry(&file, entry, scan_blockdev) == 0) {
                  if ((major(entry->device) == major(device)) &&
                      blockdev == scan_blockdev) {
                        rc = 0;
                        break;
                  }
                  proc_dev_free_entry(entry);
            } else
                  skip_line(&file);
      }
      free_file_buffer(&file);
      return rc;
}

Generated by  Doxygen 1.6.0   Back to index