/*
 * SPDX-FileCopyrightText: 2026 Didier Kryn <kryn@in2p3.fr>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
/* Read some device properties */
/* read uevent file in current directory. If not found, search upward in the */
/* directory tree for a directory containing such a file.                    */
/* in case of success, fill a structure containing device's type and name,   */
/* major and minor numbers, and return the path to that directory.           */
/* In case of failure, return NULL and don't touch the structure.            */
/* Rationale: invoke this function with a path of sysfs representing a       */
/* device, to get its properties, or invoke it with the directory just above */
/* to get the properties of the parent device. The directory just above is   */
/* not guaranteed to contain a device, this is why get_device_prop() goes    */
/* upward to find one which describes a device (contains a uevent file).     */

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "slash_sys.h"
extern size_t strLcpy(char *dst, const char *src, size_t dest_size);
static dev_prop *devmatch(const char *uevent_pathname, const char *type);
/* device_path_name is the path name of a subdirectory of /sys/devices.        */
/* If it is a symlink, it is first converted to its real path.                 */
/* Read device properties (devtype, devname, major, minor) from the uevent     */
/* file in this subdirectory. If the file is not found, and its type does not  */
/* match the second argument, meaning the directory does not represent a of    */
/* this type, or not even a device, then go upper in the directory tree until  */
/* such a match is found is found. In case of failure return NULL, else the    */
/* actual path name to the device. If the second argument is a null pointer or */
/* an empty string, then the first device found matches.                       */
/* In case of sucess, the struct pointed to by dp contains device properties.  */
/* If NULL is returned, the struct pointed to by dp is left unchanged.         */
char *get_device_prop(const char *device_path_name,       /* initial path name */
                      const char *type,      /* optional requested device type */
                      dev_prop *dp)
{
  static char buf[PATH_MAX];
  dev_prop *dprop;
  char ubuf[PATH_MAX];
  char *dpath, *upath;
  size_t size;
  /*----------------------- sanitize the device path -----------------------*/
  dpath = realpath(device_path_name, buf);
  if(!dpath || strlen(dpath)<13) return NULL;
  /* Walk the directory tree up to a path length of 12, starting in the */
  /* given directory. Stop if a uevent file is found in a directory.    */
  for( ;
       dpath && strlen(dpath) > 12;
       dpath = canonpath(dpath, "..", buf) )
    {
      upath = canonpath(dpath, "uevent", ubuf);
      if( upath && (dprop=devmatch(upath, type)) ) break;
    }
  if(!upath || !dprop ) return 0;
  *dp = *dprop;
  return dpath;
}
/*---------------------- decode the uevent file  ---------------------------*/
dev_prop *devmatch(const char *uevent_pathname, const char *type)
{
  char line[64];
  FILE *pf;
  static dev_prop dp;
  int n;
  char *c;
  dp.dn[0] = dp.dt[0] = '\0';
  dp.major = dp.minor = 0;
  n = 0;
  pf = fopen(uevent_pathname, "r");
  if(!pf) return 0;
  while( fgets(line, 32, pf) && n != 4 )
    {
      c = strchr(line, '=');
      if(!c) continue; /* should not happen */
      *c = 0;
      c++;
      {
        char *d;
        for(d=c; *d; d++) { if(*d=='\n') *d=0; } /* remove trailing newline */
      }
      n++;
      if     ( !strcmp(line, "DEVTYPE") ) strLcpy(dp.dt, c, 32);
      else if( !strcmp(line, "DEVNAME") ) strLcpy(dp.dn, c, 32);
      else if( !strcmp(line,  "MAJOR" ) ) sscanf(c, "%u", &dp.major);
      else if( !strcmp(line,  "MINOR" ) ) sscanf(c, "%u", &dp.minor);
      else n--; /* undo the increment above. */
      /* If we found one of the 4 fields we look for, then n is incremented. */
      /* we stop parsing uevent file when/if we got all 4 */
    }
  fclose(pf);
  if( type && type[0] && strcmp(type, dp.dt) ) return 0;
  else return &dp;
}
