1/***************************************************************************
2 *
3 * hotplug.c : HAL-internal hotplug events
4 *
5 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
6 * Use is subject to license terms.
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 **************************************************************************/
11
12
13#ifdef HAVE_CONFIG_H
14#  include <config.h>
15#endif
16
17#include <stdio.h>
18#include <string.h>
19#include <errno.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/un.h>
23#include <sys/utsname.h>
24#include <unistd.h>
25
26#include <glib.h>
27#include <dbus/dbus.h>
28#include <dbus/dbus-glib.h>
29
30#include "../osspec.h"
31#include "../logger.h"
32#include "../hald.h"
33#include "../device_info.h"
34
35#include "osspec_solaris.h"
36#include "hotplug.h"
37#include "devinfo.h"
38
39/** Queue of ordered hotplug events */
40GQueue *hotplug_event_queue;
41
42/** List of HotplugEvent objects we are currently processing */
43GSList *hotplug_events_in_progress = NULL;
44
45static void hotplug_event_begin (HotplugEvent *hotplug_event);
46
47void
48hotplug_event_end (void *end_token)
49{
50	HotplugEvent *hotplug_event = (HotplugEvent *) end_token;
51
52	hotplug_events_in_progress = g_slist_remove (hotplug_events_in_progress, hotplug_event);
53	g_free (hotplug_event);
54	hotplug_event_process_queue ();
55}
56
57static void
58hotplug_event_begin_devfs_add (HotplugEvent *hotplug_event, HalDevice *d)
59{
60	HalDevice *parent;
61	const gchar *parent_udi;
62	void (*begin_add_func) (HalDevice *, HalDevice *, DevinfoDevHandler *, void *);
63
64	if (d != NULL) {
65		/* XXX */
66		HAL_ERROR (("devpath %s already present in store, ignore event", hotplug_event->un.devfs.devfs_path));
67
68		goto out;
69	}
70
71	/* find parent */
72	parent_udi = hal_device_property_get_string (hotplug_event->d, "info.parent");
73	if (parent_udi == NULL || strlen(parent_udi) == 0) {
74		parent = NULL;
75	} else {
76		parent = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", parent_udi);
77	}
78	/* only root node is allowed to be orphan */
79	if (parent == NULL) {
80		if (strcmp(hotplug_event->un.devfs.devfs_path, "/") != 0) {
81			HAL_ERROR (("Parent is NULL devfs_path=%s parent_udi=%s", hotplug_event->un.devfs.devfs_path, parent_udi ? parent_udi : "<null>"));
82
83			goto out;
84		}
85	}
86
87	/* children of ignored parent should be ignored */
88	if (parent != NULL && hal_device_property_get_bool (parent, "info.ignore")) {
89		HAL_INFO (("parent ignored %s", parent_udi));
90
91		goto out;
92	}
93
94	/* custom or generic add function */
95	begin_add_func = hotplug_event->un.devfs.handler->hotplug_begin_add;
96	if (begin_add_func == NULL) {
97		begin_add_func = hotplug_event_begin_add_devinfo;
98	}
99	begin_add_func (hotplug_event->d,
100			 parent,
101			 hotplug_event->un.devfs.handler,
102			 (void *) hotplug_event);
103	 return;
104
105out:
106	g_object_unref (hotplug_event->d);
107	hotplug_event_end ((void *) hotplug_event);
108
109	return;
110}
111
112static void
113hotplug_event_begin_devfs_remove (HotplugEvent *hotplug_event, HalDevice *d)
114{
115	if (d == NULL) {
116		HAL_ERROR (("devpath %s not present in store, ignore event", hotplug_event->un.devfs.devfs_path));
117		hotplug_event_end ((void *) hotplug_event);
118		return;
119	}
120	HAL_INFO (("hotplug_event_begin_devfs_remove %s", hal_device_get_udi (d)));
121
122	hotplug_event_begin_remove_devinfo(d,
123			 hotplug_event->un.devfs.devfs_path,
124			 (void *) hotplug_event);
125}
126
127static void
128hotplug_event_begin_devfs (HotplugEvent *hotplug_event)
129{
130	HalDevice *d;
131
132	HAL_INFO (("hotplug_event_begin_devfs: %s", hotplug_event->un.devfs.devfs_path));
133	d = hal_device_store_match_key_value_string (hald_get_gdl (),
134						"solaris.devfs_path",
135						hotplug_event->un.devfs.devfs_path);
136
137	if (hotplug_event->action == HOTPLUG_ACTION_ADD) {
138		hotplug_event_begin_devfs_add (hotplug_event, d);
139	} else if (hotplug_event->action == HOTPLUG_ACTION_REMOVE) {
140		hotplug_event_begin_devfs_remove (hotplug_event, d);
141	} else {
142		HAL_ERROR (("unsupported action %d", hotplug_event->action));
143		g_object_unref (hotplug_event->d);
144		hotplug_event_end ((void *) hotplug_event);
145	}
146}
147
148static void
149hotplug_event_begin (HotplugEvent *hotplug_event)
150{
151	switch (hotplug_event->type) {
152
153	case HOTPLUG_EVENT_DEVFS:
154		hotplug_event_begin_devfs (hotplug_event);
155		break;
156
157	default:
158		HAL_ERROR (("Unknown hotplug event type %d", hotplug_event->type));
159		g_object_unref (hotplug_event->d);
160		hotplug_event_end ((void *) hotplug_event);
161		break;
162	}
163}
164
165void
166hotplug_event_enqueue (HotplugEvent *hotplug_event, int front)
167{
168	if (hotplug_event_queue == NULL)
169		hotplug_event_queue = g_queue_new ();
170
171	if (front) {
172		g_queue_push_head (hotplug_event_queue, hotplug_event);
173	} else {
174		g_queue_push_tail (hotplug_event_queue, hotplug_event);
175	}
176}
177
178void
179hotplug_event_process_queue (void)
180{
181	HotplugEvent *hotplug_event;
182
183	if (hotplug_events_in_progress == NULL &&
184	    (hotplug_event_queue == NULL || g_queue_is_empty (hotplug_event_queue))) {
185		hotplug_queue_now_empty ();
186		goto out;
187	}
188
189	/* do not process events if some other event is in progress */
190	if (hotplug_events_in_progress != NULL && g_slist_length (hotplug_events_in_progress) > 0)
191		goto out;
192
193	hotplug_event = g_queue_pop_head (hotplug_event_queue);
194	if (hotplug_event == NULL)
195		goto out;
196
197	hotplug_events_in_progress = g_slist_append (hotplug_events_in_progress, hotplug_event);
198	hotplug_event_begin (hotplug_event);
199
200out:
201	;
202}
203