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

common.c

00001 /***************************************************************************
00002  * CVSID: $Id: common.c,v 1.4 2004/04/21 18:44:41 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 "../logger.h"
00046 #include "../device_store.h"
00047 #include "../device_info.h"
00048 #include "../hald.h"
00049 #include "common.h"
00050 
00064 double
00065 parse_double (const char *str)
00066 {
00068     return atof (str);
00069 }
00070 
00077 dbus_int32_t
00078 parse_dec (const char *str)
00079 {
00080     dbus_int32_t value;
00081     value = strtol (str, NULL, 10);
00083     return value;
00084 }
00085 
00093 dbus_int32_t
00094 parse_hex (const char *str)
00095 {
00096     dbus_int32_t value;
00097     value = strtol (str, NULL, 16);
00099     return value;
00100 }
00101 
00113 long int
00114 find_num (char *pre, char *s, int base)
00115 {
00116     char *where;
00117     int result;
00118 
00119     where = strstr (s, pre);
00120     if (where == NULL) {
00121         /*DIE(("Didn't find '%s' in '%s'", pre, s)); */
00122         return LONG_MAX;
00123     }
00124     where += strlen (pre);
00125 
00126     result = strtol (where, NULL, base);
00128 /*
00129     if( result==LONG_MIN || result==LONG_MAX )
00130         DIE(("Error parsing value for '%s' in '%s'", pre, s));
00131 */
00132 
00133     return result;
00134 }
00135 
00146 double
00147 find_double (char *pre, char *s)
00148 {
00149     char *where;
00150     double result;
00151 
00152     where = strstr (s, pre);
00154     if (where == NULL)
00155         DIE (("Didn't find '%s' in '%s'", pre, s));
00156     where += strlen (pre);
00157 
00158     result = atof (where);
00159 
00160     return result;
00161 }
00162 
00173 int
00174 find_bcd2 (char *pre, char *s)
00175 {
00176     int i;
00177     char c;
00178     int digit;
00179     int left, right, result;
00180     int len;
00181     char *str;
00182     dbus_bool_t passed_white_space;
00183     int num_prec;
00184 
00185     str = find_string (pre, s);
00186     if (str == NULL || strlen (str) == 0)
00187         return 0xffff;
00188 
00189 
00190     left = 0;
00191     len = strlen (str);
00192     passed_white_space = FALSE;
00193     for (i = 0; i < len && str[i] != '.'; i++) {
00194         if (isspace (str[i])) {
00195             if (passed_white_space)
00196                 break;
00197             else
00198                 continue;
00199         }
00200         passed_white_space = TRUE;
00201         left *= 16;
00202         c = str[i];
00203         digit = (int) (c - '0');
00204         left += digit;
00205     }
00206     i++;
00207     right = 0;
00208     num_prec = 0;
00209     for (; i < len; i++) {
00210         if (isspace (str[i]))
00211             break;
00212         if (num_prec == 2)  /* Only care about two digits 
00213                      * of precision */
00214             break;
00215         right *= 16;
00216         c = str[i];
00217         digit = (int) (c - '0');
00218         right += digit;
00219         num_prec++;
00220     }
00221 
00222     for (; num_prec < 2; num_prec++)
00223         right *= 16;
00224 
00225     result = left * 256 + (right & 255);
00226     return result;
00227 }
00228 
00239 char *
00240 find_string (char *pre, char *s)
00241 {
00242     char *where;
00243     static char buf[256];
00244     char *p;
00245 
00246     where = strstr (s, pre);
00247     if (where == NULL)
00248         return NULL;
00249     where += strlen (pre);
00250 
00251     p = buf;
00252     while (*where != '\n' && *where != '\r') {
00253         char c = *where;
00254 
00255         /* ignoring char 63 fixes a problem with info from the 
00256          * Lexar CF Reader
00257          */
00258         if ((isalnum (c) || isspace (c) || ispunct (c)) && c != 63) {
00259             *p = c;
00260             ++p;
00261         }
00262 
00263         ++where;
00264     }
00265     *p = '\0';
00266 
00267     /* remove trailing white space */
00268     --p;
00269     while (isspace (*p)) {
00270         *p = '\0';
00271         --p;
00272     }
00273 
00274     return buf;
00275 }
00276 
00284 char *
00285 read_single_line (char *filename_format, ...)
00286 {
00287     FILE *f;
00288     int i;
00289     int len;
00290     char filename[512];
00291     static char buf[512];
00292     va_list args;
00293 
00294     va_start (args, filename_format);
00295     vsnprintf (filename, 512, filename_format, args);
00296     va_end (args);
00297 
00298     f = fopen (filename, "rb");
00299     if (f == NULL)
00300         return NULL;
00301 
00302     if (fgets (buf, 512, f) == NULL) {
00303         fclose (f);
00304         return NULL;
00305     }
00306 
00307     len = strlen (buf);
00308     for (i = len - 1; i > 0; --i) {
00309         if (buf[i] == '\n' || buf[i] == '\r')
00310             buf[i] = '\0';
00311         else
00312             break;
00313     }
00314 
00315     fclose (f);
00316     return buf;
00317 }
00318 
00325 const char *
00326 get_last_element (const char *s)
00327 {
00328     int len;
00329     const char *p;
00330 
00331     len = strlen (s);
00332     for (p = s + len - 1; p > s; --p) {
00333         if ((*p) == '/')
00334             return p + 1;
00335     }
00336 
00337     return s;
00338 }
00339 
00340 /* returns the path of the udevinfo program 
00341  *
00342  * @return                      path or NULL if udevinfo program is not found
00343  */
00344 const char *
00345 udevinfo_path (void)
00346 {
00347     char *possible_paths[] = { "/sbin/udevinfo",
00348         "/usr/bin/udevinfo",
00349         "/usr/sbin/udevinfo",
00350         "/usr/local/sbin/udevinfo"
00351     };
00352     char *path = NULL;
00353     unsigned int i;
00354     struct stat s;
00355     for (i = 0; i < sizeof (possible_paths) / sizeof (char *); i++) {
00356         if (stat (possible_paths[i], &s) == 0
00357             && S_ISREG (s.st_mode)) {
00358             path = possible_paths[i];
00359             break;
00360         }
00361     }
00362     return path;
00363 }
00364 
00391 char *
00392 rename_and_merge (HalDevice * d,
00393           ComputeFDI naming_func, const char *namespace)
00394 {
00395     int append_num;
00396     char *computed_udi;
00397     HalDevice *computed_d;
00398 
00399     /* udi is a temporary udi */
00400 
00401     append_num = -1;
00402 tryagain:
00403     /* compute the udi for the device */
00404     computed_udi = (*naming_func) (d, append_num);
00405 
00406     /* See if a device with the computed udi already exist. It can exist
00407      * because the device-list is (can be) persistent across invocations 
00408      * of hald.
00409      *
00410      * If it does exist, note that it's udi is computed from only the same 
00411      * information as our just computed udi.. So if we match, and it's
00412      * unplugged, it's the same device!
00413      *
00414      * (of course assuming that our udi computing algorithm actually works!
00415      *  Which it might not, see discussions - but we can get close enough
00416      *  for it to be practical)
00417      */
00418     computed_d = hal_device_store_find (hald_get_gdl (), computed_udi);
00419     if (computed_d != NULL) {
00420 
00421         if ((!hal_device_has_property
00422              (computed_d, "info.not_available"))
00423             &&
00424             (!hal_device_property_get_bool
00425              (computed_d, "info.not_available"))) {
00426             /* Danger, Will Robinson! Danger!
00427              *
00428              * Ok, this means that either
00429              *
00430              * a) The user plugged in two instances of the kind of
00431              *    of device; or
00432              *
00433              * b) The agent is invoked with --probe for the second
00434              *    time during the life of the HAL daemon
00435              *
00436              * We want to support b) otherwise we end up adding a
00437              * lot of devices which is a nuisance.. We also want to
00438              * be able to do b) when developing HAL agents.
00439              *
00440              * So, therefore we check if the non-unplugged device 
00441              * has the same bus information as our newly hotplugged
00442              * one.
00443              */
00444             if (hal_device_matches (computed_d, d, namespace)) {
00445                 HAL_ERROR (("Found device already present "
00446                         "as '%s'!\n", computed_d->udi));
00447                 hal_device_print (d);
00448                 hal_device_print (computed_d);
00449                 /* indeed a match, must be b) ;ignore device */
00450                 hal_device_store_remove (hald_get_tdl (), d);
00451                 g_object_unref (d);
00452                 /* and return */
00453                 return NULL;
00454             }
00455             
00458             append_num++;
00459             goto tryagain;
00460         } else {
00461             /* must be another instance of this type of device */
00462             append_num++;
00463             goto tryagain;
00464         }
00465 
00466         /* It did exist! Merge our properties from the probed device
00467          * since some of the bus-specific properties are likely to be
00468          * different 
00469          *
00470          * (user may have changed port, #Dev is different etc.)
00471          *
00472          * Note that the probed device only contain bus-specific
00473          * properties - the other properties will remain..
00474          */
00475         hal_device_merge (computed_d, d);
00476 
00477         /* Set that we are back in business! */
00478         hal_device_property_set_bool (computed_d, "info.not_available",
00479                           FALSE);
00480 
00481         HAL_INFO (("Device %s is plugged in again",
00482                computed_d->udi));
00483 
00484     } else {
00485         /* Device is not in list... */
00486 
00487         /* assign the computed device name */
00488         /*HAL_INFO ((" ##### computed_udi=%s", computed_udi));*/
00489         hal_device_set_udi (d, computed_udi);
00490         hal_device_property_set_string (d, "info.udi", computed_udi);
00491 
00492         /* Search for device information file and attempt merge */
00493         if (di_search_and_merge (d)) {
00494             HAL_INFO (("Found a .fdi file for %s", d->udi));
00495         }
00496 
00497     }
00498 
00499     return computed_udi;
00500 }
00501 
00508 char *
00509 get_parent_sysfs_path (const char *path)
00510 {
00511     int i;
00512     int len;
00513     char *parent_path;
00514 
00515     /* Find parent device by truncating our own path */
00516     parent_path = strndup (path, SYSFS_PATH_MAX);
00517     if (parent_path == NULL)
00518         DIE (("No memory"));
00519     len = strlen (parent_path);
00520     for (i = len - 1; parent_path[i] != '/'; --i) {
00521         parent_path[i] = '\0';
00522     }
00523     parent_path[i] = '\0';
00524 
00525     return parent_path;
00526 }
00527 
00536 void
00537 find_and_set_physical_device (HalDevice * device)
00538 {
00539     HalDevice *d;
00540     HalDevice *parent;
00541     const char *parent_udi;
00542 
00543     d = device;
00544     do {
00545         parent_udi = hal_device_property_get_string (d,
00546                                   "info.parent");
00547         if (parent_udi == NULL) {
00548             HAL_ERROR (("Error finding parent for %s\n",
00549                     d->udi));
00550             return;
00551         }
00552 
00553         parent = hal_device_store_find (hald_get_gdl (), parent_udi);
00554         if (parent == NULL) {
00555             HAL_ERROR (("Error resolving UDI %s\n",
00556                     parent_udi));
00557             return;
00558         }
00559 
00560         if (!hal_device_has_property (parent,
00561                            "info.physical_device")) {
00562             hal_device_property_set_string (device,
00563                              "info.physical_device",
00564                              parent_udi);
00565             return;
00566         }
00567 
00568         d = parent;
00569     }
00570     while (TRUE);
00571 }
00572 
00584 dbus_bool_t
00585 class_device_get_major_minor (const char *sysfs_path, int *major, int *minor)
00586 {
00587     struct sysfs_attribute *attr;
00588     char attr_path[SYSFS_PATH_MAX];
00589 
00590     snprintf (attr_path, SYSFS_PATH_MAX, "%s/dev", sysfs_path);
00591     attr = sysfs_open_attribute (attr_path);
00592     if (sysfs_read_attribute (attr) >= 0) {
00593         if (sscanf (attr->value, "%d:%d", major, minor) != 2) {
00594             HAL_WARNING (("Could not parse '%s'", attr->value));
00595             sysfs_close_attribute (attr);
00596             return TRUE;
00597         }
00598         sysfs_close_attribute (attr);
00599         return TRUE;
00600     }
00601 
00602     return FALSE;           
00603 }
00604 
00613 dbus_bool_t
00614 class_device_get_device_file (const char *sysfs_path, 
00615                   char *dev_file, int dev_file_length)
00616 {
00617     int i;
00618     unsigned int sysfs_mount_path_len;
00619     char sysfs_path_trunc[SYSFS_NAME_LEN];
00620     char *udev_argv[7] = { (char *) udevinfo_path (), 
00621                    "-r", "-q", "name", "-p",
00622                    sysfs_path_trunc, NULL };
00623     char *udev_stdout;
00624     char *udev_stderr;
00625     int udev_exitcode;
00626 
00627     /* compute truncated sysfs path - udevinfo doesn't want the
00628      * the sysfs_mount_path (e.g. /udev or /dev) prefix */
00629     sysfs_mount_path_len = strlen (sysfs_mount_path);
00630     if (strlen (sysfs_path) > sysfs_mount_path_len) {
00631         strncpy (sysfs_path_trunc, sysfs_path + sysfs_mount_path_len,
00632              SYSFS_NAME_LEN);
00633     }
00634 
00635     /*HAL_INFO (("*** sysfs_path_trunc = '%s'", sysfs_path_trunc));*/
00636 
00637     /* Now invoke udevinfo */
00638     if (udev_argv[0] == NULL || g_spawn_sync ("/",
00639                           udev_argv,
00640                           NULL,
00641                           0,
00642                           NULL,
00643                           NULL,
00644                           &udev_stdout,
00645                           &udev_stderr,
00646                           &udev_exitcode,
00647                           NULL) != TRUE) {
00648         HAL_ERROR (("Couldn't invoke %s", udevinfo_path ()));
00649         return FALSE;
00650     }
00651 
00652     if (udev_exitcode != 0) {
00653         HAL_ERROR (("%s returned %d", udevinfo_path (),
00654                 udev_exitcode));
00655         return FALSE;
00656     }
00657 
00658     /* sanitize string returned by udev */
00659     for (i = 0; udev_stdout[i] != 0; i++) {
00660         if (udev_stdout[i] == '\r' || udev_stdout[i] == '\n') {
00661             udev_stdout[i] = 0;
00662             break;
00663         }
00664     }
00665 
00666     /*HAL_INFO (("got device file %s for %s", udev_stdout, sysfs_path));*/
00667 
00668     strncpy (dev_file, udev_stdout, dev_file_length);
00669     return TRUE;
00670 }
00671 
00672 
00673 /* Entry in bandaid driver database */
00674 struct driver_entry_s {
00675     char driver_name[SYSFS_NAME_LEN];
00677     char device_path[SYSFS_PATH_MAX];
00679     struct driver_entry_s *next;   
00681 };
00682 
00684 static struct driver_entry_s *drivers_table_head = NULL;
00685 
00691 static void
00692 drivers_add_entry (const char *driver_name, const char *device_path)
00693 {
00694     struct driver_entry_s *entry;
00695 
00696     entry = malloc (sizeof (struct driver_entry_s));
00697     if (entry == NULL)
00698         DIE (("Out of memory"));
00699     strncpy (entry->driver_name, driver_name, SYSFS_NAME_LEN);
00700     strncpy (entry->device_path, device_path, SYSFS_PATH_MAX);
00701     entry->next = drivers_table_head;
00702     drivers_table_head = entry;
00703 }
00704 
00712 const char *
00713 drivers_lookup (const char *device_path)
00714 {
00715     struct driver_entry_s *i;
00716 
00717     for (i = drivers_table_head; i != NULL; i = i->next) {
00718         if (strcmp (device_path, i->device_path) == 0)
00719             return i->driver_name;
00720     }
00721     return NULL;
00722 }
00723 
00729 void
00730 drivers_collect (const char *bus_name)
00731 {
00732     char path[SYSFS_PATH_MAX];
00733     struct sysfs_directory *current;
00734     struct sysfs_link *current2;
00735     struct sysfs_directory *dir;
00736     struct sysfs_directory *dir2;
00737 
00738     /* traverse /sys/bus/<bus>/drivers */
00739     snprintf (path, SYSFS_PATH_MAX, "%s/bus/%s/drivers",
00740           sysfs_mount_path, bus_name);
00741     dir = sysfs_open_directory (path);
00742     if (dir == NULL) {
00743         HAL_WARNING (("Error opening sysfs directory at %s\n",
00744                   path));
00745         goto out;
00746     }
00747     if (sysfs_read_directory (dir) != 0) {
00748         HAL_WARNING (("Error reading sysfs directory at %s\n",
00749                   path));
00750         goto out;
00751     }
00752     if (dir->subdirs != NULL) {
00753         dlist_for_each_data (dir->subdirs, current,
00754                      struct sysfs_directory) {
00755             /*printf("name=%s\n", current->name); */
00756 
00757             dir2 = sysfs_open_directory (current->path);
00758             if (dir2 == NULL)
00759                 DIE (("Error opening sysfs directory "
00760                       "at %s\n", current->path));
00761             if (sysfs_read_directory (dir2) != 0)
00762                 DIE (("Error reading sysfs directory "
00763                       "at %s\n", current->path));
00764 
00765             if (dir2->links != NULL) {
00766                 dlist_for_each_data (dir2->links, current2,
00767                              struct sysfs_link) {
00768                     /*printf("link=%s\n",current2->target);
00769                      */
00770                     drivers_add_entry (current->name,
00771                                current2->
00772                                target);
00773                 }
00774                 sysfs_close_directory (dir2);
00775             }
00776         }
00777     }
00778 out:
00779     if (dir != NULL)
00780         sysfs_close_directory (dir);
00781 }
00782 

Generated on Sat Apr 24 19:57:44 2004 for HAL by doxygen 1.3.6-20040222