pcmciad.c revision 360e6f5e7a29d5950aa1985f56811731715da7e5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PCMCIA User Daemon
* This is a newer daemon than the original version.
* It is a restructuring to use multiple threads
* and LWP in order to not lose any events and
* to better be able to service devices. It has
* hooks for using external processing functions
* of various types.
*
* If you add a new built-in handler, please keep its functions
* grouped together.
*
* The prototype external shared object code assumes that you
* have three entry points defined. It doesn't load global.
* The entry points are:
* void handler(char *, char *, int, int, void *)
* void init(void)
* void fini(void)
*
* If init() doesn't exist it isn't called and fini() currently
* isn't ever called. handler() must exist. They must use these
* these specific names.
*
* init() is called just before the first time the handler is to
* be called. This allows any initial setup that might be
* necessary. It is only called once.
*
* External handlers can reference symbols in the main program
* but the symbols will be invisible across handlers.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define _KERNEL
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <stropts.h>
#include <sys/sservice.h>
#include <string.h>
#include <search.h>
#include <ftw.h>
#include <pwd.h>
#include <grp.h>
/* memory card support headers */
#include <errno.h>
#include <dirent.h>
#include <signal.h>
#include <limits.h>
#include <thread.h>
#include <synch.h>
#include <dlfcn.h>
/*
* pcmciad
*
* the basic daemon is a multithreaded entity that listens for
* PCMCIA events and acts on them.
* one thread just reads the event driver and constructs work requests
* on a per-socket work queue. A wakeup is given to the work thread
* for that work queue. When the work thread wakes up, it starts
* processing its queue one item at a time until the queue is empty.
* It then goes to sleep and waits to get another work item.
*
* One additional thread is used for processing special commands
* that need to be serialized in order to avoid problems. The list of
* commands is currently "drvconfig, disks".
*
* note that the work to be done is driver dependent.
* The daemon makes every attempt to identify what needs to be done
* and call the driver specific functions. If nothing is found then
* a default behavior is used (modload the driver and then call
* devlinks is the built in behavior)
*/
/*
* The macros defined here that are used to specify the encoding
* of the socket and function number fields must match the
* functionality of the similar macros defined in cs_priv.h
* Since this daemon code is going to be completely replaced in
* Solaris 2.7 by the generic DDI hotplug framework, no
* effort was made to include cs_priv.h
* The following is taken from cs_priv.h:
* The encoding of the socket number is:
*
* xxxxxxxx | xxxxgfff | xxxxxxxx | xsssssss
*
* g - global CIS bit
* f - function number bit
* s - socket number bit
* x - don't care bits
*/
/*
* daemon global variables and datatypes
*/
/* prototypes for per driver functions */
static void serial(char *, char *, int, int, void *);
static void serinit(void);
static void defhandler(char *, char *, int, int, void *);
typedef void (* function_t)(char *, char *, int, int, void *);
typedef void (* if_func_t)(void);
static void memory(char *, char *, int, int, void *);
static void meminit(void);
static void disk(char *, char *, int, int, void *);
static void diskinit(void);
#define MAX_DRIVERS 128
#define DRV_NOT_INIT 0x0002
#define DRV_NOT_LOAD 0x0001
#define DRV_SHARED_OBJ 0x0004
#define DRV_SHELL_SCRIPT 0x0008
struct driver_specific {
char *driver;
char *class;
void *handle;
};
};
static int num_drivers = 3;
struct work {
union em_primitives *prim;
};
struct tasklist {
};
struct driver_aliases {
struct driver_aliases *next;
char *name;
char *alias;
};
#define DBG_ALL 0xffff
#define DBG_SETUP 0x0001
#define DBG_MODLOAD 0x0002
#define DBG_TASK 0x0004
#define DBG_DEVSETUP 0x0008
#define DBG_SYSCALL 0x0010
#define DBG_ALIASING 0x0080
static int debug = 0;
static int pem; /* event manager descriptor */
/*
* Global mutexes to single-thread certain card handler
* routines.
*/
static void init_global_locks();
#ifndef lint
static mutex_t serinit_lock;
static mutex_t meminit_lock;
#endif
#define DEFAULT_DIR "/usr/lib/pcmcia"
static char *default_dir = DEFAULT_DIR;
static char *pcmcia_root_dir = "/devices";
#ifndef lint
static char *pcmcia_driver_class = "root";
#endif
static int num_sockets; /* current number of sockets */
static int task_state = 0; /* zero is not running */
/* prototypes used internally */
static char *find_alias(char *);
static void init_aliases(char *);
static void drv_init_table(char *);
static void em_init(int);
static void setup_autopush(char *);
static void *event_read(void *);
static void task_create(int);
static void task_init(int);
int
{
int c;
switch (c) {
case 'D':
break;
case 'd':
break;
case 'l':
break;
default:
"usage: pcmciad [-D] [-d device]\n");
exit(1);
}
}
if (debug)
(void) chdir(default_dir);
(void) init_aliases("/etc/driver_aliases");
(void) drv_init_table(default_dir);
(void) init_global_locks();
if (pem < 0) {
if (debug)
exit(2);
}
if (!debug) {
(void) close(0);
(void) close(1);
(void) close(2);
if (fork() == 0) {
(void) setsid();
} else {
exit(0);
}
} else {
}
(void) task_init(num_sockets);
#ifndef lint
if (debug) {
(void) printf("concurrency == %d\n",
}
#endif
/*
* the per-socket threads are up and running
* so we now start the work finding task
*/
#ifdef lint
(void *) event_read(0);
#else
exit(0x20);
#endif
(void) sigfillset(&sigs);
#ifndef lint
perror("thr_sigsetmask");
#endif
(void) sigpause(1);
return (0x22);
}
/*
* alias handling
* we need to be able to find the mapping between certain
* device names and the appropriate driver. This code
* searches the driver alias list. It needs to know when
* to re-read and reconstruct the list in the event of new
* aliases being added.
*/
/*
* init_aliases(filename)
* read in the aliases file and construct a fast lookup
* list. We don't want to read every time since that could
* take too long in multiple event conditions.
*/
static void
init_aliases(char *path)
{
int i, len;
struct driver_aliases *nalias;
!= EOF) {
if (alias[0] == '"') {
for (i = 1; i < len; i++)
}
break;
if (debug & DBG_ALIASING) {
(void) printf("add alias: %s <- %s\n",
}
}
}
}
/*
* find_alias(name)
* look up name in the alias list and return either the alias
* or NULL (if none found).
*/
char *
find_alias(char *name)
{
struct driver_aliases *alias;
if (debug & DBG_ALIASING) {
(void) printf("alias: %s == %s [name = %s]\n",
}
}
return (NULL);
}
/*
* driver handler lookup, initialization functions
*/
static void
drv_init_table(char *dir)
{
return;
}
for (; num_drivers < MAX_DRIVERS; ) {
break;
/* skip dot names */
continue;
/*
* not a .so so skip for now
* later could be a shell script or
* other scripting function or a
* directory for class specific
* drivers
*/
if (debug & DBG_DEVSETUP) {
(void) printf("Skipping: %s\n",
}
continue;
}
/*
* as support is added for different types of objects,
* the appropriate check and setup should be done
* here. Some possible future additions:
* .sh (shell), .pl (perl) and .exe (executable)
*/
/*
* we have an object so claim it
*/
found = 1;
flags = DRV_NOT_INIT |
}
if (found) {
/* want to replace if new module found */
0)
break;
if (debug & DBG_DEVSETUP)
(void) printf("Found %s (%s) "
"as shared object\n",
num_drivers++;
}
}
}
static int
{
void *handle;
char path[256];
return (0);
return (0);
/* now have a handle so get the three functions */
return (1);
}
return (0);
}
static void *
find_handler(char *name)
{
char *alias;
int i;
for (i = 0; i < num_drivers; i ++) {
if (debug)
(void) printf(
"found handler for %s "
"(flags=%x)\n",
driver_map[i].driver,
driver_map[i].flags);
(void) printf("load driver\n");
if (drv_load(&driver_map[i])) {
driver_map[i].init();
driver_map[i].flags &=
}
}
driver_map[i].init();
}
return ((void *)driver_map[i].handler);
}
}
}
return ((void *) defhandler);
}
/*
* fix_device_path(root, dev)
* for a new device that was created during a MODCONFIG, it
* is necessary to either run "drvconfig" which takes too long
* or patch the new name which ended up with a ".." prepended
* to the name.
*/
static void
{
== NULL) {
if (debug & DBG_DEVSETUP) {
perror("pcmciad: fixup_devnames malloc");
}
return;
}
== NULL) {
if (debug) {
perror("pcmciad: fixup_devnames malloc");
}
return;
}
if (debug & DBG_DEVSETUP) {
}
return;
lastpart++;
if (debug & DBG_DEVSETUP)
/* get last component to replace */
if (debug & DBG_DEVSETUP) {
(void) printf("\tfixup_devnames: starting at [%s]\n",
rootdir);
(void) printf("fixup_devnames: rename(%s, %s)\n",
}
}
/*
* initialize the event interface.
*/
static void
{
int flags, i;
char buff[1024];
}
/* LINTED lint won't shut up about this line */
/* LINTED lint won't shut up about this line */
perror("putmsg");
exit(2);
}
(void) printf("\tsent EM_INIT_REQ\n");
}
flags = 0;
perror("em_init: getmsg");
exit(3);
}
(void) printf("\treceived primitive: %d\n",
(int)init->em_primitive);
}
/* LINTED lint won't shut up about this line */
perror("putmsg");
exit(2);
}
(void) printf("\tsent EM_ADAPTER_INFO_REQ\n");
}
flags = 0;
perror("em_init: getmsg");
exit(3);
}
/* LINTED lint won't shut up about this line */
(void) printf("adapter info:\n");
(void) printf("\tsockets: %d\n",
(int)infoack->em_num_sockets);
(void) printf("\twindows: %d\n",
(int)infoack->em_num_windows);
}
for (i = 0; i < num_sockets; i++) {
em_ident_socket_req_t *ident;
int f;
for (f = 0; f < PCMCIA_MAX_FUNCTIONS; f++) {
/* LINTED lint won't shut up about this line */
ident = (em_ident_socket_req_t *)buff;
ident->em_primitive = EM_IDENT_SOCKET_REQ;
perror("putmsg");
}
}
}
} else {
(void) printf("\treceived primitive: %d\n",
(int)infoack->em_primitive);
}
}
}
/*
* event_read(fd)
* read event from the event manager driver.
* This is run as a bound thread so that it won't ever
* have to wait for a thread to become available.
* This is the whole driving force behind the tasks.
*/
static char cbuff[4096];
static char dbuff[4096];
static void *
event_read(void *xx)
{
union em_primitives *prim;
int running;
/*
* before doing anything else, make sure other threads
* won't kill us with spurious signals
*/
(void) sigfillset(&sigs);
#ifndef lint
perror("thr_sigsetmask");
#endif
int flags = 0;
perror("event_read: getmsg");
exit(0x10);
} else
continue;
}
/* LINTED lint won't shut up about this line */
"unexpected indicator\n");
} else {
/*
* now have an event indicator
* find the socket and dup the message then
* queue the message onto the work list
*/
continue;
}
continue;
}
(void) printf("socket %d: function %d about to "
"queue work\n",
(int)
(int)
}
#ifndef lint
#endif
(void) printf("...done\n");
#ifndef lint
#endif
}
}
/* NOTREACHED */
return ((void *) NULL);
}
/*
* event_process(socket, prim)
* when a socket thread wakes up and finds work
* it calls this function to decode the event
* indication. Currently, only events are processed
* Only the sockert number is passed in the "socket"
* arguemnt - the function number is not encoded
* in the "socket" argument.
*/
static void
{
int function, i;
char *name = "*";
char *class = "*";
(void) printf("event_process(%d, %d, %x)\n",
}
switch (prim->em_primitive) {
/* general "event" indication */
case EM_EVENT_IND:
/*
* some events are handled in here directly
* or are done here and in the specific code
*/
(void) printf("event = %d\n",
}
case PCE_CARD_INSERT:
/*
* card insertion must be preprocessed
* in the daemon common code since we
* don't know the drivers yet.
* it is a time for cleanup. The driver
* specific code will get this on ident.
*/
for (i = 0; i < PCMCIA_MAX_FUNCTIONS; i++)
return;
case PCE_CARD_REMOVAL:
/*
* driver specific processing is
* all we need to do. cleanup on insert.
*/
break;
case PCE_DEV_IDENT:
/*
* do a modload to make things start running.
* this only does something the first time
* but that time is needed.
*/
/*
* now identify the driver and setup
* per driver function info
*/
break;
case PCE_INIT_DEV:
/* LINTED lint won't shut up about this line */
if (debug & DBG_DEVSETUP) {
(void) printf("prim=%x, offset = %x (%s)\n",
(int)prim,
"create" : "remove");
}
if (debug & DBG_DEVSETUP)
break;
default:
break;
}
}
}
/*
* task_socket(socket)
* this is the thread code for per-socket tasks.
* it will check for work and sleep on its condition
* variable when there is no work.
*/
static void *
task_socket(void *socket_val)
{
int socket = (int)socket_val;
while (task_state) {
(void) printf(
"task socket(%d) checking for work %x:%x\n",
}
#ifndef lint
/* nothing to do so go to sleep */
}
#endif
(void) printf("task socket(%d) have work\n",
socket);
}
/* we have a work item so get it and remove from list */
#ifndef lint
#endif
/* we have work item isolated so process it */
}
return (0);
}
/*
* task_create(socket)
* create a task (structure and thread) for specified socket
*/
static void
task_create(int socket)
{
int err = 0;
#ifndef lint
#endif
/*
* note that the work list is empty so the thread will
* immediately block in a wait on the condition.
* nothing will add a work item until all threads are
* created so no need to start it suspended
*/
#ifdef lint
(void *) task_socket(0);
if (err) {
#else
#endif
errno = 0;
"nothread on %d (err=%d)\n",
perror("thr_create");
}
}
/*
* task_init(sockets)
* given the number of sockets, go through and initialize
* a task list and thread for them.
*/
static void
{
int i;
/*
* in order to deal with new sockets coming online use
* sockets * 2 as the preallocated list. We may have to
* revisit if we can get more than twice the number dynamically.
*/
(num_sockets * 2));
exit(4);
for (i = 0; i < sockets; i++) {
(void) task_create(i);
}
}
/*
* defhandler(driver, class, event, socket, data)
* default handler for drivers that don't need much
*/
static void
{
(void) printf("defhandler(%s, %s, %x, %x, %x)\n",
}
switch (event) {
case PCE_INIT_DEV:
break;
}
}
/* serial handler functions */
#define SER_TERM_UID_DEFAULT 0 /* root UID */
#define SER_TERM_USER_DEFAULT "root"
#define SER_TERM_GROUP_DEFAULT "sys"
#define SER_CUA_USER_DEFAULT "uucp"
#define SER_CUA_GROUP_DEFAULT "uucp"
static int ser_mode_term;
static int ser_term_uid;
static int ser_term_gid;
static int ser_mode_cua;
static int ser_cua_uid;
static int ser_cua_gid;
static void
serinit()
{
#ifndef lint
(void) mutex_lock(&serinit_lock);
#endif
setpwent();
setgrent();
/*
* Setup the dial-in devices
* to these.
*/
} /* getgrnam */
} /* getpwnam */
/*
* Setup the dial-out devices
* to these.
*/
} /* getgrnam */
} /* getpwnam */
endgrent();
endpwent();
#ifndef lint
(void) mutex_unlock(&serinit_lock);
#endif
}
/*
* serial(driver, class, event, socket, data)
* default serial device handler
*/
static void
{
char path[MAXPATHLEN];
char device[MODMAXNAMELEN];
#ifdef lint
#endif
(void) printf("serial(0x%x, 0x%x, 0x%x)\n",
}
switch (event) {
case PCE_INIT_DEV:
{
break;
dir = "cua";
} else {
dir = "term";
}
(void) printf("symlink(%s, %s)\n",
}
debug & DBG_SYSCALL) {
char error[64];
device);
}
perror("chmod");
perror("chown");
}
break;
}
}
static void
setup_autopush(char *driver)
{
#ifdef lint
return;
#else
int sadfd;
&major_num)) < 0) {
if (debug & DBG_SYSCALL) {
perror("modctl(MODGETMAJBIND)");
}
return;
} else {
if (debug & DBG_SYSCALL) {
(void) printf("\tdriver = [%s], major_num = %d\n",
}
if (debug & DBG_SYSCALL) {
perror("open(ADMINDEV)");
}
return;
} else {
if (debug & DBG_SYSCALL) {
perror("\tioctl(SAD_SAP)");
}
}
}
}
#endif
}
/*
* Common define for the memory() and disk() function handler
*/
#define VOLDSK_PATH "/vol/dev/dsk"
#define VOLRDSK_PATH "/vol/dev/rdsk"
#define DEVDSK_PATH "/dev/dsk"
#define DEVRDSK_PATH "/dev/rdsk"
#define START_CHAR 'c'
/* code for the memory() function handler */
#define VOLD_TYPE_PCRAM "pcram"
#define PCMEM_PATH "/pcmem"
static void unmount_media(long, char *);
static void signal_vold(long, char *);
int volmgt_running();
char *media_findname(char *);
/*
* memory(driver, class, event, socket data)
* this is the default memory card handler
*/
static void
meminit()
{
static void makepdir();
#ifndef lint
#endif
(void) makepdir();
#ifndef lint
#endif
}
static void
{
#ifdef lint
#endif
(void) printf("memory(%x, %x, %x)\n",
}
switch (event) {
case PCE_INIT_DEV:
(void) printf("Run disks cmd for memory\n");
}
}
break;
case PCE_DEV_IDENT:
break;
case PCE_CARD_REMOVAL:
break;
}
}
/* code for the disk() function handler */
/*
* Note that PSARC/1994/238 requires to use
* "/pcdisk" directory name instead of
* "/pcata"
*/
#define PCDISK_PATH "/pcdisk"
/*
* disk(driver, class, event, socket data)
* this is the default disk card handler
*/
static void
diskinit()
{
/*
*/
/*
* XXX Don't forget to consider whether or not you need to
* single-thread this code like serinit and meminit
* does by using a global mutex!
*/
}
static void
{
#ifdef lint
#endif
(void) printf("disk(%x, %x, %x)\n",
}
switch (event) {
case PCE_INIT_DEV:
(void) printf("Run disks cmd for disk\n");
}
}
/*
* signal_vold(...);
*/
break;
case PCE_DEV_IDENT:
break;
case PCE_CARD_REMOVAL:
/*
* unmount_media(...);
*/
break;
}
}
/*
* get_devrdsk
* Given cn[tn]dnsn or cn[tn] (vold alias) path,
* to see if it matches with device_type and
* the socket information.
*
*/
static const char *
{
char namebuf[MAXNAMELEN];
char linkbuf[MAXNAMELEN];
int linksize;
int found;
int gotit;
int i, searchlen;
if (debug) {
(void) printf(
"get_devrdsk: Error opening directory %s\n",
}
return (NULL);
}
/*
* skip . and .. and
* anything else starting with dot)
*/
continue;
}
/*
* Silently Ignore for now any names not
* stating with START_CHAR
*/
continue;
}
/*
* Skip if path [cntn] is not a subset of
* dskentp->d_name [cntndnsn]
*/
continue;
}
/*
* found a name that matches!
*/
if (debug) {
(void) printf("\tget_devrdsk: Cannot stat %s\n",
namebuf);
}
continue;
}
found = 0;
if (linksize <= 0) {
if (debug) {
(void) printf("\tget_devrdsk: "
"Could not read symbolic link %s\n",
namebuf);
}
continue;
}
if (debug & DBG_DEVSETUP) {
(void) printf(
"\tget_devrdsk: check path %s\n",
linkbuf);
}
/* Make sure it is a right device_type */
continue;
/*
* If it is VOLD_TYPE_PCRAM, search backward
* until '@' character, then a number
* after '@' is a socket number
*
* ../../devices/../\
* MemoryAliasName@SocketN[,FunctN]/\
* pcram:tn,dn:dev,raw
*/
gotit = 0;
for (i = searchlen; i > 0; i--) {
if (*devp == '@') {
gotit++;
break;
}
devp--;
}
if (gotit) {
/* Get socket info. */
devp++;
if (debug & DBG_DEVSETUP) {
(void) printf(
"\tget_devrdsk: devp=%s\n", devp);
}
found++;
/* exit readdir() loop */
break;
}
} else {
if (debug) {
(void) printf(
"\tget_devrdsk: Invalid path [%s] "
"for device_type [%s]\n",
}
}
} /* if (S_ISLNK) */
} /* while (dskentp) */
}
/*
* unmount_media - Unmount PCMCIA media file system
*
* If the user accidentally removes the card without
* using eject(1) command, this routine is called for unmounting
* a mounted file system assuming that the directory is not busy
*/
static void
{
static void start_unmount(char *, char *);
const char *nvp;
char pname[100];
int dnlen;
/* mtab is gone... let it go */
goto out;
}
strlen(PCMEM_PATH));
/*
* Skip if mnt_special is not a VOLDSK_PATH
* or DEVDSK_PATH
*/
if ((isit_voldp == 0) && (isit_pcmemp == 0) ||
(isit_devp == 0)) {
/*
* extract cn[tn]dnsn from
*/
if (isit_voldp == 0) {
}
!= NULL) {
}
}
}
out:
}
/*
* start_unmount - Start to unmount mounting directory
*
* Using mnt_special for unmounting vold path, and
* mnt_mountp for regular umount(1M)
*/
static void
{
static int req_vold_umount(char *);
static int do_umount(char *);
int err = 0;
/*
* If vold is running we have to request the vold
* to unmount the file system (sigh!) in order to
* to clean up /vol enviroment (?)
*/
#ifdef lint
/* LINTED */
if (0) {
#else
if (volmgt_running()) {
#endif
if (req_vold_umount(mnt_special) == 0) {
err++;
}
} else {
/*
* Great! we can do a simple umount(1M)
* if the vold is not runing by umount <mnt_mountp>
* (including /pcmem/<mnt_mountp> after
* vold is disabled
*
* OR when the user removes the memory card WITHOUT
* using eject(1) command
*/
if (do_umount(mnt_mountp) == 0) {
err++;
}
}
}
}
/*
* req_vold_umount - Request vold to unmount
*
* If vold is running, this routine is called when the user
* removes a PC card WITHOUT using eject(1) command
*/
static int
req_vold_umount(char *path)
{
int fd;
int rval = 0;
const char *rawpath;
/* Convert to "raw" path (rdsk) for DKIOCEJECT ioctl() */
#ifdef lint
#else
#endif
if (debug) {
(void) printf(
"\treq_vold_umount: %s is busy\n",
rawpath);
}
}
goto out;
}
if (debug) {
(void) printf(
"\treq_vold_umount: Unmount vold path [%s]\n",
rawpath);
}
/*
* This simulates the volmgt eject(1) command
* its enviroment after unmount so we can use the same
* slot for different PC card
*/
/* suppose to see ENOSYS from pcmem driver */
/* or ENOENT since card is gone */
/* Could be EBUSY [16] (Mount device busy) */
if (debug) {
(void) printf(
"\treq_vold_umount: DKIOCEJECT errno [%d]\n",
errno);
}
goto out;
}
}
rval = 1;
out:
return (rval);
}
/*
* do_umount - Unmount a file system when volmgt is not running
*/
static int
{
int fd;
/*
* Use fork1 instead since this is a Solaris thread program
*/
/* the child */
/* get rid of those nasty err messages */
return (-1);
}
/* the parent */
/* wait for the umount command to exit */
if (debug) {
path);
}
return (1);
}
/*
* get_rdsk_path
* Given /devices/.. path, return cntndnsn path in
*/
static const char *
get_rdsk_path(char *dev)
{
/* get the dev_t for our target */
dev);
if (debug) {
(void) printf(
"\tget_rdsk_path: error: can't stat %s (%s)\n",
}
goto dun;
}
/* Must be a raw device */
if (debug) {
(void) printf(
"\tget_rdsk_path: Not a raw device [%s]\n",
path_buf);
}
goto dun;
}
/* scan the disks directory for the "right device" */
if (debug) {
(void) printf(
"\terror: can't open directory %s (%s)\n",
}
goto dun;
}
continue;
}
if (debug) {
(void) printf(
"\terror: can't stat \"%s\" (%s)\n",
}
continue;
}
continue;
}
/* found it! */
if (debug) {
(void) printf(
"\tget_rdsk_path: found \"%s\"\n",
res);
}
break;
}
dun:
}
return (res);
}
/*
* signal_vold - tell vold that a new path has been added
*/
static void
{
static void wr_to_pipe(char *, char *, int);
static const char *get_rdsk_path(char *);
const char *rpath;
if (debug) {
(void) printf("\tsignal_vold: entering for \"%s\"\n",
device);
}
#ifndef lint
/* Do not write to the pipe if vold is not running */
if (volmgt_running() == 0) {
if (debug) {
(void) printf("\tsignal_vold: volmgt NOT running\n");
}
return;
}
#endif
/*
* disks(1) command does not work correctly
* or the devices can not be found in devfs tree
*/
if (debug) {
("\tsignal_vold: error - get NULL devpath\n"));
}
return;
}
}
static void
{
static int fd = -1;
/* open a named pipe without blocking */
if (fd < 0) {
/*
* May be reader process does NOT open
* the other end ofthe pipe yet
*/
if (debug) {
(void) printf(
"wr_to_pipe: open(\"%s\") failed (errno %d, NO reader?)\n",
PCRAM_FILE, errno);
}
return;
}
}
if (debug) {
}
}
/*
* Create PCMCIA pipe directory
*/
static void
makepdir()
{
extern int errno;
/* Make a pipe directory */
if (debug) {
}
if (debug) {
"error: can't create pipe directory %s (%s)\n"),
}
return;
} /* !EEXIST */
} /* mkdir */
/* Make a fifo special named pipe file */
if (debug) {
}
if (debug) {
"error: can't create named pipe %s (%s)\n"),
}
}
}
}
static void
{
#ifndef lint
#endif
}