Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

linux_common.c

00001 /***************************************************************************
00002  * CVSID: $Id: linux_common.c,v 1.11 2004/03/05 14:40:58 david Exp $
00003  *
00004  * linux_common.c : Common functionality used by Linux specific parts
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  *
00008  * Licensed under the Academic Free License version 2.0
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  **************************************************************************/
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #define _GNU_SOURCE 1       /* for strndup() */
00031 
00032 #include <ctype.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <getopt.h>
00037 #include <assert.h>
00038 #include <unistd.h>
00039 #include <stdarg.h>
00040 #include <limits.h>
00041 
00042 #include <sys/types.h>
00043 #include <sys/stat.h>
00044 
00045 #include <libhal/libhal.h>  /* For HAL_STATE_* */
00046 
00047 #include "../logger.h"
00048 #include "../device_store.h"
00049 #include "../device_info.h"
00050 #include "linux_common.h"
00051 
00065 double
00066 parse_double (const char *str)
00067 {
00069     return atof (str);
00070 }
00071 
00078 dbus_int32_t
00079 parse_dec (const char *str)
00080 {
00081     dbus_int32_t value;
00082     value = strtol (str, NULL, 10);
00084     return value;
00085 }
00086 
00094 dbus_int32_t
00095 parse_hex (const char *str)
00096 {
00097     dbus_int32_t value;
00098     value = strtol (str, NULL, 16);
00100     return value;
00101 }
00102 
00114 long int
00115 find_num (char *pre, char *s, int base)
00116 {
00117     char *where;
00118     int result;
00119 
00120     where = strstr (s, pre);
00121     if (where == NULL) {
00122         /*DIE(("Didn't find '%s' in '%s'", pre, s)); */
00123         return LONG_MAX;
00124     }
00125     where += strlen (pre);
00126 
00127     result = strtol (where, NULL, base);
00129 /*
00130     if( result==LONG_MIN || result==LONG_MAX )
00131         DIE(("Error parsing value for '%s' in '%s'", pre, s));
00132 */
00133 
00134     return result;
00135 }
00136 
00147 double
00148 find_double (char *pre, char *s)
00149 {
00150     char *where;
00151     double result;
00152 
00153     where = strstr (s, pre);
00155     if (where == NULL)
00156         DIE (("Didn't find '%s' in '%s'", pre, s));
00157     where += strlen (pre);
00158 
00159     result = atof (where);
00160 
00161     return result;
00162 }
00163 
00174 int
00175 find_bcd2 (char *pre, char *s)
00176 {
00177     int i;
00178     char c;
00179     int digit;
00180     int left, right, result;
00181     int len;
00182     char *str;
00183     dbus_bool_t passed_white_space;
00184     int num_prec;
00185 
00186     str = find_string (pre, s);
00187     if (str == NULL || strlen (str) == 0)
00188         return 0xffff;
00189 
00190 
00191     left = 0;
00192     len = strlen (str);
00193     passed_white_space = FALSE;
00194     for (i = 0; i < len && str[i] != '.'; i++) {
00195         if (isspace (str[i])) {
00196             if (passed_white_space)
00197                 break;
00198             else
00199                 continue;
00200         }
00201         passed_white_space = TRUE;
00202         left *= 16;
00203         c = str[i];
00204         digit = (int) (c - '0');
00205         left += digit;
00206     }
00207     i++;
00208     right = 0;
00209     num_prec = 0;
00210     for (; i < len; i++) {
00211         if (isspace (str[i]))
00212             break;
00213         if (num_prec == 2)  /* Only care about two digits 
00214                      * of precision */
00215             break;
00216         right *= 16;
00217         c = str[i];
00218         digit = (int) (c - '0');
00219         right += digit;
00220         num_prec++;
00221     }
00222 
00223     for (; num_prec < 2; num_prec++)
00224         right *= 16;
00225 
00226     result = left * 256 + (right & 255);
00227     return result;
00228 }
00229 
00240 char *
00241 find_string (char *pre, char *s)
00242 {
00243     char *where;
00244     static char buf[256];
00245     char *p;
00246 
00247     where = strstr (s, pre);
00248     if (where == NULL)
00249         return NULL;
00250     where += strlen (pre);
00251 
00252     p = buf;
00253     while (*where != '\n' && *where != '\r') {
00254         char c = *where;
00255 
00256         /* ignoring char 63 fixes a problem with info from the 
00257          * Lexar CF Reader
00258          */
00259         if ((isalnum (c) || isspace (c) || ispunct (c)) && c != 63) {
00260             *p = c;
00261             ++p;
00262         }
00263 
00264         ++where;
00265     }
00266     *p = '\0';
00267 
00268     /* remove trailing white space */
00269     --p;
00270     while (isspace (*p)) {
00271         *p = '\0';
00272         --p;
00273     }
00274 
00275     return buf;
00276 }
00277 
00285 char *
00286 read_single_line (char *filename_format, ...)
00287 {
00288     FILE *f;
00289     int i;
00290     int len;
00291     char filename[512];
00292     static char buf[512];
00293     va_list args;
00294 
00295     va_start (args, filename_format);
00296     vsnprintf (filename, 512, filename_format, args);
00297     va_end (args);
00298 
00299     f = fopen (filename, "rb");
00300     if (f == NULL)
00301         return NULL;
00302 
00303     if (fgets (buf, 512, f) == NULL) {
00304         fclose (f);
00305         return NULL;
00306     }
00307 
00308     len = strlen (buf);
00309     for (i = len - 1; i > 0; --i) {
00310         if (buf[i] == '\n' || buf[i] == '\r')
00311             buf[i] = '\0';
00312         else
00313             break;
00314     }
00315 
00316     fclose (f);
00317     return buf;
00318 }
00319 
00326 const char *
00327 get_last_element (const char *s)
00328 {
00329     int len;
00330     const char *p;
00331 
00332     len = strlen (s);
00333     for (p = s + len - 1; p > s; --p) {
00334         if ((*p) == '/')
00335             return p + 1;
00336     }
00337 
00338     return s;
00339 }
00340 
00341 /* returns the path of the udevinfo program 
00342  *
00343  * @return                      path or NULL if udevinfo program is not found
00344  */
00345 const char *
00346 udevinfo_path (void)
00347 {
00348     char *possible_paths[] = { "/sbin/udevinfo",
00349         "/usr/bin/udevinfo",
00350         "/usr/sbin/udevinfo",
00351         "/usr/local/sbin/udevinfo"
00352     };
00353     char *path = NULL;
00354     unsigned int i;
00355     struct stat s;
00356     for (i = 0; i < sizeof (possible_paths) / sizeof (char *); i++) {
00357         if (stat (possible_paths[i], &s) == 0
00358             && S_ISREG (s.st_mode)) {
00359             path = possible_paths[i];
00360             break;
00361         }
00362     }
00363     return path;
00364 }
00365 
00392 char *
00393 rename_and_merge (HalDevice * d,
00394           ComputeFDI naming_func, const char *namespace)
00395 {
00396     int append_num;
00397     char *computed_udi;
00398     HalDevice *computed_d;
00399 
00400     /* udi is a temporary udi */
00401 
00402     append_num = -1;
00403       tryagain:
00404     /* compute the udi for the device */
00405     computed_udi = (*naming_func) (d, append_num);
00406 
00407     /* See if a device with the computed udi already exist. It can exist
00408      * because the device-list is (can be) persistent across invocations 
00409      * of hald.
00410      *
00411      * If it does exist, note that it's udi is computed from only the same 
00412      * information as our just computed udi.. So if we match, and it's
00413      * unplugged, it's the same device!
00414      *
00415      * (of course assuming that our udi computing algorithm actually works!
00416      *  Which it might not, see discussions - but we can get close enough
00417      *  for it to be practical)
00418      */
00419     computed_d = ds_device_find (computed_udi);
00420     if (computed_d != NULL) {
00421 
00422         if ((!ds_property_exists
00423              (computed_d, "info.not_available"))
00424             &&
00425             (!ds_property_get_bool
00426              (computed_d, "info.not_available"))) {
00427             /* Danger, Will Robinson! Danger!
00428              *
00429              * Ok, this means that either
00430              *
00431              * a) The user plugged in two instances of the kind of
00432              *    of device; or
00433              *
00434              * b) The agent is invoked with --probe for the second
00435              *    time during the life of the HAL daemon
00436              *
00437              * We want to support b) otherwise we end up adding a
00438              * lot of devices which is a nuisance.. We also want to
00439              * be able to do b) when developing HAL agents.
00440              *
00441              * So, therefore we check if the non-unplugged device 
00442              * has the same bus information as our newly hotplugged
00443              * one.
00444              */
00445             if (ds_device_matches (computed_d, d, namespace)) {
00446                 HAL_ERROR (("Found device already present "
00447                         "as '%s'!\n", computed_d->udi));
00448                 ds_print (d);
00449                 ds_print (computed_d);
00450                 /* indeed a match, must be b) ;ignore device */
00451                 ds_device_destroy (d);
00452                 /* and return */
00453                 return NULL;
00454             }
00455             
00458             append_num++;
00459             goto tryagain;
00460         }
00461 
00462         /* It did exist! Merge our properties from the probed device
00463          * since some of the bus-specific properties are likely to be
00464          * different 
00465          *
00466          * (user may have changed port, #Dev is different etc.)
00467          *
00468          * Note that the probed device only contain bus-specific
00469          * properties - the other properties will remain..
00470          */
00471         ds_device_merge (computed_d, d);
00472 
00473         /* Remove temporary device */
00474         ds_device_destroy (d);
00475 
00476         /* Set that we are back in business! */
00477         ds_property_set_bool (computed_d, "info.not_available",
00478                       FALSE);
00479 
00480         HAL_INFO (("Device %s is plugged in again",
00481                computed_d->udi));
00482 
00483     } else {
00484         /* Device is not in list... */
00485 
00486         /* assign the computed device name */
00487         ds_device_set_udi (d, computed_udi);
00488         ds_property_set_string (d, "info.udi", computed_udi);
00489 
00490         /* Search for device information file and attempt merge */
00491         if (di_search_and_merge (d)) {
00492             HAL_INFO (("Found a .fdi file for %s", d->udi));
00493         }
00494 
00495     }
00496 
00497     return computed_udi;
00498 }
00499 
00506 char *
00507 get_parent_sysfs_path (const char *path)
00508 {
00509     int i;
00510     int len;
00511     char *parent_path;
00512 
00513     /* Find parent device by truncating our own path */
00514     parent_path = strndup (path, SYSFS_PATH_MAX);
00515     if (parent_path == NULL)
00516         DIE (("No memory"));
00517     len = strlen (parent_path);
00518     for (i = len - 1; parent_path[i] != '/'; --i) {
00519         parent_path[i] = '\0';
00520     }
00521     parent_path[i] = '\0';
00522 
00523     return parent_path;
00524 }
00525 
00534 void
00535 find_and_set_physical_device (HalDevice * device)
00536 {
00537     HalDevice *d;
00538     HalDevice *parent;
00539     const char *parent_udi;
00540 
00541     d = device;
00542     do {
00543         parent_udi = ds_property_get_string (d, "info.parent");
00544         if (parent_udi == NULL) {
00545             HAL_ERROR (("Error finding parent for %s\n",
00546                     d->udi));
00547             return;
00548         }
00549 
00550         parent = ds_device_find (parent_udi);
00551         if (parent == NULL) {
00552             HAL_ERROR (("Error resolving UDI %s\n",
00553                     parent_udi));
00554             return;
00555         }
00556 
00557         if (!ds_property_exists (parent, "info.physical_device")) {
00558             ds_property_set_string (device,
00559                         "info.physical_device",
00560                         parent_udi);
00561             return;
00562         }
00563 
00564         d = parent;
00565     }
00566     while (TRUE);
00567 }
00568 
00569 
00570 /* Entry in bandaid driver database */
00571 struct driver_entry_s {
00572     char driver_name[SYSFS_NAME_LEN];
00574     char device_path[SYSFS_PATH_MAX];
00576     struct driver_entry_s *next;   
00578 };
00579 
00581 static struct driver_entry_s *drivers_table_head = NULL;
00582 
00588 static void
00589 drivers_add_entry (const char *driver_name, const char *device_path)
00590 {
00591     struct driver_entry_s *entry;
00592 
00593     entry = malloc (sizeof (struct driver_entry_s));
00594     if (entry == NULL)
00595         DIE (("Out of memory"));
00596     strncpy (entry->driver_name, driver_name, SYSFS_NAME_LEN);
00597     strncpy (entry->device_path, device_path, SYSFS_PATH_MAX);
00598     entry->next = drivers_table_head;
00599     drivers_table_head = entry;
00600 }
00601 
00609 const char *
00610 drivers_lookup (const char *device_path)
00611 {
00612     struct driver_entry_s *i;
00613 
00614     for (i = drivers_table_head; i != NULL; i = i->next) {
00615         if (strcmp (device_path, i->device_path) == 0)
00616             return i->driver_name;
00617     }
00618     return NULL;
00619 }
00620 
00626 void
00627 drivers_collect (const char *bus_name)
00628 {
00629     char path[SYSFS_PATH_MAX];
00630     struct sysfs_directory *current;
00631     struct sysfs_link *current2;
00632     struct sysfs_directory *dir;
00633     struct sysfs_directory *dir2;
00634 
00635     /* traverse /sys/bus/<bus>/drivers */
00636     snprintf (path, SYSFS_PATH_MAX, "%s/bus/%s/drivers",
00637           sysfs_mount_path, bus_name);
00638     dir = sysfs_open_directory (path);
00639     if (dir == NULL) {
00640         HAL_WARNING (("Error opening sysfs directory at %s\n",
00641                   path));
00642         goto out;
00643     }
00644     if (sysfs_read_directory (dir) != 0) {
00645         HAL_WARNING (("Error reading sysfs directory at %s\n",
00646                   path));
00647         goto out;
00648     }
00649     if (dir->subdirs != NULL) {
00650         dlist_for_each_data (dir->subdirs, current,
00651                      struct sysfs_directory) {
00652             /*printf("name=%s\n", current->name); */
00653 
00654             dir2 = sysfs_open_directory (current->path);
00655             if (dir2 == NULL)
00656                 DIE (("Error opening sysfs directory "
00657                       "at %s\n", current->path));
00658             if (sysfs_read_directory (dir2) != 0)
00659                 DIE (("Error reading sysfs directory "
00660                       "at %s\n", current->path));
00661 
00662             if (dir2->links != NULL) {
00663                 dlist_for_each_data (dir2->links, current2,
00664                              struct sysfs_link) {
00665                     /*printf("link=%s\n",current2->target);
00666                      */
00667                     drivers_add_entry (current->name,
00668                                current2->
00669                                target);
00670                 }
00671                 sysfs_close_directory (dir2);
00672             }
00673         }
00674     }
00675 out:
00676     if (dir != NULL)
00677         sysfs_close_directory (dir);
00678 }
00679 

Generated on Thu Mar 11 21:32:22 2004 for HAL by doxygen 1.3.6-20040222