1 /***************************************************************************
2  *
3  * sysevent.c : Solaris sysevents
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 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15 
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sys/dkio.h>
23 #include <sys/stat.h>
24 #include <libdevinfo.h>
25 #include <libsysevent.h>
26 #include <sys/sysevent/dev.h>
27 #include <sys/sysevent/pwrctl.h>
28 #include <sys/sysevent/dr.h>
29 #include <glib.h>
30 #include <config_admin.h>
31 #include <kstat.h>
32 
33 #include "../osspec.h"
34 #include "../logger.h"
35 #include "../hald.h"
36 #include "../hald_dbus.h"
37 #include "../device_info.h"
38 #include "../util.h"
39 #include "osspec_solaris.h"
40 #include "hotplug.h"
41 #include "devinfo.h"
42 #include "devinfo_storage.h"
43 #include "devinfo_acpi.h"
44 #include "devinfo_usb.h"
45 #include "sysevent.h"
46 #include "devinfo_misc.h"
47 #include "devinfo_cpu.h"
48 
49 #ifndef ESC_LOFI
50 #define	ESC_LOFI "lofi"
51 #endif
52 
53 static void	sysevent_dev_handler(sysevent_t *);
54 static gboolean sysevent_iochannel_data(GIOChannel *, GIOCondition, gpointer);
55 static void	sysevent_dev_add(gchar *, gchar *);
56 static void	sysevent_dev_remove(gchar *, gchar *);
57 static void	sysevent_dev_branch(gchar *);
58 static void	sysevent_lofi_add(gchar *, gchar *);
59 static void	sysevent_lofi_remove(gchar *, gchar *);
60 static void	sysevent_devfs_add(gchar *);
61 static void	sysevent_pwrctl(gchar *, gchar *, gchar *, gchar *, gchar *,
62 		    gchar *, uint_t);
63 static void	sysevent_process_dr(gchar *, gchar *);
64 
65 static sysevent_handle_t	*shp;
66 
67 static int sysevent_pipe_fds[2];
68 static GIOChannel *sysevent_iochannel;
69 static guint sysevent_source_id;
70 
71 gboolean
72 sysevent_init(void)
73 {
74 	GError *err = NULL;
75 	const char	*subcl[6];
76 
77 	/*
78 	 * pipe used to serialize sysevents through the main loop
79 	 */
80 	if (pipe (sysevent_pipe_fds) != 0) {
81 		HAL_INFO (("pipe() failed errno=%d", errno));
82 		return (FALSE);
83 	}
84 	sysevent_iochannel = g_io_channel_unix_new (sysevent_pipe_fds[0]);
85 	if (sysevent_iochannel == NULL) {
86 		HAL_INFO (("g_io_channel_unix_new failed"));
87 		return (FALSE);
88 	}
89 	g_io_channel_set_flags (sysevent_iochannel, G_IO_FLAG_NONBLOCK, &err);
90 	sysevent_source_id = g_io_add_watch (
91 	    sysevent_iochannel, G_IO_IN, sysevent_iochannel_data, NULL);
92 
93 	shp = sysevent_bind_handle(sysevent_dev_handler);
94 	if (shp == NULL) {
95 		HAL_INFO (("sysevent_bind_handle failed %d", errno));
96 		return (FALSE);
97 	}
98 
99 	subcl[0] = ESC_DISK;
100 	subcl[1] = ESC_LOFI;
101 	subcl[2] = ESC_PRINTER;
102 	if (sysevent_subscribe_event(shp, EC_DEV_ADD, subcl, 3) != 0) {
103 		HAL_INFO (("subscribe(dev_add) failed %d", errno));
104 		sysevent_unbind_handle(shp);
105 		return (FALSE);
106 	}
107 	if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subcl, 3) != 0) {
108 		HAL_INFO (("subscribe(dev_remove) failed %d", errno));
109 		sysevent_unbind_handle(shp);
110 		return (FALSE);
111 	}
112 
113 	subcl[0] = ESC_DEV_BRANCH_REMOVE;
114 	if (sysevent_subscribe_event(shp, EC_DEV_BRANCH, subcl, 1) != 0) {
115 		HAL_INFO (("subscribe(dev_branch) failed %d", errno));
116 		sysevent_unbind_handle(shp);
117 		return (FALSE);
118 	}
119 
120 	subcl[0] = ESC_PWRCTL_ADD;
121 	subcl[1] = ESC_PWRCTL_REMOVE;
122 	subcl[2] = ESC_PWRCTL_STATE_CHANGE;
123 	subcl[3] = ESC_PWRCTL_BRIGHTNESS_UP;
124 	subcl[4] = ESC_PWRCTL_BRIGHTNESS_DOWN;
125 	subcl[5] = ESC_PWRCTL_POWER_BUTTON;
126 	if (sysevent_subscribe_event(shp, EC_PWRCTL, subcl, 6) != 0) {
127 		HAL_INFO(("subscribe(dev_add) failed %d", errno));
128 		sysevent_unbind_handle(shp);
129 		return (FALSE);
130 	}
131 
132 	subcl[0] = ESC_DEVFS_DEVI_ADD;
133 	if (sysevent_subscribe_event(shp, EC_DEVFS, subcl, 1) != 0) {
134 		HAL_INFO (("subscribe(EC_DEVFS) failed %d", errno));
135 		sysevent_unbind_handle(shp);
136 		return (FALSE);
137 	}
138 
139 	subcl[0] = ESC_DR_AP_STATE_CHANGE;
140 	if (sysevent_subscribe_event(shp, EC_DR, subcl, 1) != 0) {
141 		HAL_INFO (("subscribe(dynamic reconfiguration) failed %d",
142 		    errno));
143 		sysevent_unbind_handle(shp);
144 		return (FALSE);
145 	}
146 
147 	return (B_TRUE);
148 }
149 
150 void
151 sysevent_fini(void)
152 {
153 	sysevent_unbind_handle(shp);
154 	shp = NULL;
155 }
156 
157 static void
158 sysevent_dev_handler(sysevent_t *ev)
159 {
160 	char		*class;
161 	char		*subclass;
162 	nvlist_t	*attr_list;
163 	char		*phys_path;
164 	char		*dev_name;
165 	char		*dev_hid;
166 	char		*dev_uid;
167 	uint_t		dev_index;
168 	char		s[1024];
169 	ssize_t		nwritten;
170 
171 	if ((class = sysevent_get_class_name(ev)) == NULL)
172 		return;
173 
174 	if ((subclass = sysevent_get_subclass_name(ev)) == NULL)
175 		return;
176 
177 	if (sysevent_get_attr_list(ev, &attr_list) != 0)
178 		return;
179 
180 	if (strcmp(class, EC_DEVFS) == 0) {
181 		if (nvlist_lookup_string(attr_list, DEVFS_PATHNAME, &phys_path) != 0) {
182 			goto out;
183 		}
184 
185 		snprintf(s, sizeof (s), "%s %s %s\n",
186 		    class, subclass, phys_path);
187 		nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
188 
189 		HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
190 		goto out;
191 	}
192 
193 	if (strcmp(class, EC_PWRCTL) == 0) {
194 		if (nvlist_lookup_string(attr_list, PWRCTL_DEV_PHYS_PATH,
195 		    &phys_path) != 0) {
196 			goto out;
197 		}
198 	} else if (strcmp(class, EC_DR) == 0) {
199 		if (nvlist_lookup_string(attr_list, DR_AP_ID,
200 		    &phys_path) != 0) {
201 			goto out;
202 		}
203 	} else if (nvlist_lookup_string(attr_list, DEV_PHYS_PATH, &phys_path)
204 	    != 0) {
205 		goto out;
206 	}
207 
208 	/*
209 	 * In case of EC_DR, use dev_name to store DR_HINT val
210 	 */
211 	if (strcmp(class, EC_DR) == 0) {
212 		if (nvlist_lookup_string(attr_list, DR_HINT, &dev_name) != 0) {
213 			goto out;
214 		}
215 	} else if (nvlist_lookup_string(attr_list, DEV_NAME, &dev_name) != 0) {
216 		if (strcmp(class, EC_PWRCTL) == 0) {
217 			dev_name = "noname";
218 		} else {
219 			dev_name = "";
220 		}
221 	}
222 
223 	if (nvlist_lookup_string(attr_list, PWRCTL_DEV_HID, &dev_hid) != 0) {
224 		dev_hid = "";
225 	}
226 	if (nvlist_lookup_string(attr_list, PWRCTL_DEV_UID, &dev_uid) != 0) {
227 		dev_uid = "";
228 	}
229 	if (nvlist_lookup_uint32(attr_list, PWRCTL_DEV_INDEX, &dev_index)
230 	    != 0) {
231 		dev_index = 0;
232 	}
233 
234 	snprintf(s, sizeof (s), "%s %s %s %s %s %s %d\n",
235 	    class, subclass, phys_path, dev_name, dev_hid, dev_uid, dev_index);
236 	nwritten = write(sysevent_pipe_fds[1], s, strlen(s) + 1);
237 
238 	HAL_INFO (("sysevent_dev_handler: wrote %d bytes", nwritten));
239 
240 out:
241 	nvlist_free(attr_list);
242 }
243 
244 static gboolean
245 sysevent_iochannel_data (GIOChannel *source,
246 		    GIOCondition condition,
247 		    gpointer user_data)
248 {
249 	GError *err = NULL;
250 	gchar *s = NULL;
251 	gsize len;
252 	int matches;
253 	gchar class[1024];
254 	gchar subclass[1024];
255 	gchar phys_path[1024];
256 	gchar dev_name[1024];
257 	gchar dev_uid[1024];
258 	gchar dev_hid[1024];
259 	uint_t dev_index;
260 
261 	HAL_INFO (("sysevent_iochannel_data"));
262 
263 	while (g_io_channel_read_line (sysevent_iochannel, &s, &len, NULL,
264 	    &err) == G_IO_STATUS_NORMAL) {
265 		if (len == 0) {
266 			break;
267 		}
268 		HAL_INFO (("IOChannel val => %s", s));
269 		class[0] = subclass[0] = phys_path[0] = dev_name[0] =
270 		    dev_hid[0] = dev_uid[0] = '\0';
271 		matches = sscanf(s, "%s %s %s %s %s %s %d", class, subclass,
272 		    phys_path, dev_name, dev_hid, dev_uid, &dev_index);
273 		g_free (s);
274 		s = NULL;
275 		if (matches < 3) {
276 			continue;
277 		}
278 		HAL_INFO (("sysevent: class=%s, sub=%s", class, subclass));
279 
280 		if (strcmp(class, EC_DEV_ADD) == 0) {
281 			if ((strcmp(subclass, ESC_DISK) == 0) ||
282 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
283 				sysevent_dev_add(phys_path, dev_name);
284 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
285 				sysevent_lofi_add(phys_path, dev_name);
286 			}
287 		} else if (strcmp(class, EC_DEV_REMOVE) == 0) {
288 			if ((strcmp(subclass, ESC_DISK) == 0) ||
289 			    (strcmp(subclass, ESC_PRINTER) == 0)) {
290 				sysevent_dev_remove(phys_path, dev_name);
291 			} else if (strcmp(subclass, ESC_LOFI) == 0) {
292 				sysevent_lofi_remove(phys_path, dev_name);
293 			}
294 		} else if (strcmp(class, EC_DEV_BRANCH) == 0) {
295 			sysevent_dev_branch(phys_path);
296 		} else if (strcmp(class, EC_PWRCTL) == 0) {
297 			sysevent_pwrctl(class, subclass, phys_path,
298 			    dev_name, dev_hid, dev_uid, dev_index);
299 		} else if (strcmp(class, EC_DEVFS) == 0) {
300 			if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0) {
301 				sysevent_devfs_add(phys_path);
302 			}
303 		} else if (strcmp(class, EC_DR) == 0) {
304 			/*
305 			 * Note: AP_ID is stored in phys_path and HINT is
306 			 * stored in dev_name, to avoid creating seperate
307 			 * variables and multiple conditions checking
308 			 */
309 			HAL_DEBUG (("In %s, AP_ID-> %s, Hint-> %s", class,
310 			    phys_path, dev_name));
311 			if (strcmp(subclass, ESC_DR_AP_STATE_CHANGE) == 0) {
312 				sysevent_process_dr(phys_path, dev_name);
313 			}
314 		}
315 	}
316 
317 	if (err) {
318 		g_error_free (err);
319 	}
320 
321 	return (TRUE);
322 }
323 
324 static void
325 sysevent_dev_add(gchar *devfs_path, gchar *name)
326 {
327 	gchar	*parent_devfs_path, *hotplug_devfs_path;
328 	HalDevice *parent;
329 
330 	HAL_INFO (("dev_add: %s %s", name, devfs_path));
331 
332 	parent = hal_util_find_closest_ancestor (devfs_path, &parent_devfs_path, &hotplug_devfs_path);
333 	if (parent == NULL) {
334 		return;
335 	}
336 
337 	HAL_INFO (("dev_add: parent=%s", parent_devfs_path));
338 	HAL_INFO (("dev_add: real=%s", hotplug_devfs_path));
339 
340 	devinfo_add (parent, hotplug_devfs_path);
341 
342 	g_free (parent_devfs_path);
343 	g_free (hotplug_devfs_path);
344 
345 	hotplug_event_process_queue ();
346 }
347 
348 static void
349 sysevent_dev_remove(gchar *devfs_path, gchar *name)
350 {
351 	HAL_INFO (("dev_remove: %s %s", name, devfs_path));
352 
353 	devinfo_remove_branch (devfs_path, NULL);
354 	hotplug_event_process_queue ();
355 }
356 
357 static void
358 sysevent_dev_branch(gchar *devfs_path)
359 {
360 	HAL_INFO (("branch_remove: %s", devfs_path));
361 
362 	devinfo_remove_branch (devfs_path, NULL);
363 	hotplug_event_process_queue ();
364 }
365 
366 static void
367 sysevent_lofi_add(gchar *devfs_path, gchar *name)
368 {
369 	di_node_t node;
370 	const char *parent_udi;
371 	HalDevice *d, *parent;
372 
373 	HAL_INFO (("lofi_add: %s %s", name, devfs_path));
374 
375 	if ((d = hal_device_store_match_key_value_string (hald_get_gdl (),
376 	    "solaris.devfs_path", devfs_path)) == NULL) {
377 		HAL_INFO (("device not found in GDL %s", devfs_path));
378 		return;
379 	}
380 	parent_udi = hal_device_property_get_string (d, "info.parent");
381 	if ((parent_udi == NULL) || (strlen(parent_udi) == 0)) {
382 		HAL_INFO (("parent not found in GDL %s", parent_udi));
383 		return;
384 	}
385 	if ((parent = hal_device_store_match_key_value_string (hald_get_gdl (),
386 	    "info.udi", parent_udi)) == NULL) {
387 		HAL_INFO (("parent not found in GDL %s", parent_udi));
388 		return;
389 	}
390 
391 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
392 		HAL_INFO (("device not found in devinfo %s", devfs_path));
393 		return;
394 	}
395 
396 	HAL_INFO (("device %s parent %s", hal_device_get_udi (d), parent_udi));
397 	devinfo_lofi_add_major (parent, node, devfs_path, NULL, TRUE, d);
398 
399 	di_fini (node);
400 
401 	hotplug_event_process_queue ();
402 }
403 
404 static void
405 sysevent_lofi_remove(gchar *parent_devfs_path, gchar *name)
406 {
407 	devinfo_lofi_remove_minor(parent_devfs_path, name);
408 	hotplug_event_process_queue ();
409 }
410 
411 static HalDevice *
412 lookup_parent(char *devfs_path)
413 {
414 	gchar		*path = NULL;
415 	HalDevice	*parent = NULL;
416 	char *p;
417 
418 	path = strdup (devfs_path);
419 	p = strrchr (path, '/');
420 	if (p == NULL) {
421 		free (path);
422 		return (NULL);
423 	}
424 	*p = '\0';
425 
426 	/* Look up the parent node in the gdl. */
427 	parent = hal_device_store_match_key_value_string (hald_get_gdl (),
428 	    "solaris.devfs_path", path);
429 
430 	if (parent == NULL) {
431 		/* Look up the parent node in the tdl. */
432 		parent = hal_device_store_match_key_value_string (hald_get_tdl (),
433 		    "solaris.devfs_path", path);
434 	}
435 
436 	free (path);
437 	return (parent);
438 }
439 
440 /*
441  * Handle the USB bus devices hot plugging events.
442  */
443 static void
444 sysevent_devfs_add(gchar *devfs_path)
445 {
446 	di_node_t node;
447 	HalDevice *parent;
448 	char *driver_name;
449 
450 	HAL_INFO (("devfs_handle: %s", devfs_path));
451 
452 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
453 		HAL_INFO (("device not found in devinfo %s", devfs_path));
454 		return;
455 	}
456 
457 	if ((driver_name = di_driver_name (node)) == NULL)
458 		goto out;
459 
460 	/* The disk and printer devices are handled by EC_DEV_ADD class. */
461 	if ((strcmp (driver_name, "scsa2usb") == 0) ||
462 	    (strcmp (driver_name, "usbprn") == 0))
463 		goto out;
464 
465 	if ((parent = lookup_parent (devfs_path)) == NULL)
466 		goto out;
467 
468 	devinfo_usb_add (parent, node, devfs_path, NULL);
469 
470 	di_fini (node);
471 
472 	hotplug_event_process_queue ();
473 
474 	return;
475 
476  out:
477 	di_fini (node);
478 }
479 
480 static void
481 sysevent_pwrctl(gchar *class, gchar *subclass, gchar *phys_path,
482     gchar *dev_name, gchar *dev_hid, gchar *dev_uid, uint_t dev_index)
483 {
484 	const gchar prefix[] = "/org/freedesktop/Hal/devices/pseudo/acpi_drv_0";
485 	gchar udi[HAL_PATH_MAX];
486 
487 	if (strcmp(dev_hid, "PNP0C0A") == 0) {
488 		snprintf(udi, sizeof(udi), "%s_battery%d_0", prefix, dev_index);
489 		devinfo_battery_rescan(phys_path, udi);
490 	} else if (strcmp(dev_hid, "ACPI0003") == 0) {
491 		snprintf(udi, sizeof (udi), "%s_ac%d_0", prefix, dev_index);
492 		devinfo_battery_rescan(phys_path, udi);
493 	} else if (strcmp(dev_hid, "PNP0C0D") == 0) {
494 		snprintf(udi, sizeof (udi), "%s_lid_0", prefix);
495 		devinfo_lid_event(subclass, udi);
496 	} else if (strcmp(subclass, ESC_PWRCTL_POWER_BUTTON) == 0) {
497 		devinfo_power_button_event();
498 	} else if ((strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_UP) == 0) ||
499 	    (strcmp(subclass, ESC_PWRCTL_BRIGHTNESS_DOWN) == 0)) {
500 		devinfo_brightness_hotkeys_event(subclass);
501 	} else {
502 		HAL_INFO(("Unmatched EC_PWRCTL"));
503 	}
504 }
505 
506 static void
507 sysevent_dr_remove_cpu()
508 {
509 
510 	HalDeviceStore	*gdl;
511 	GSList		*iter;
512 	HalDevice	*d, *del_dev;
513 	int		cpu_id, del_cpuid;
514 	kstat_ctl_t	*kc;
515 	kstat_t		*ksp;
516 	kstat_named_t	*ksdata;
517 	const char	*cpu_devfs_path;
518 	/*
519 	 * Find the CPU's that are DR removed. For each "processor" device in
520 	 * HAL device tree, check if it has its corresponding kstat_info. If
521 	 * not, then, that cpu has been removed and can remove the entry from
522 	 * HAL entry
523 	 */
524 
525 	HAL_DEBUG (("sysevent_dr_remove_cpu()"));
526 	kc = kstat_open ();
527 	if (kc == NULL) {
528 		HAL_INFO (("Error in removing HAL cpu entry during DR. Could"
529 		    " not open kstat to get cpu info: %s", strerror (errno)));
530 		return;
531 	}
532 
533 	/*
534 	 * Iterate through the HAL device list to get the processor devices
535 	 */
536 	gdl = hald_get_gdl ();
537 	iter = gdl->devices;
538 
539 	while (iter != NULL) {
540 		d = HAL_DEVICE (iter->data);
541 
542 		if (!hal_device_has_property (d, "processor.number")) {
543 			iter = iter->next;
544 			continue;
545 		}
546 
547 		cpu_id = hal_device_property_get_int (d, "processor.number");
548 
549 		/*
550 		 * Check if the above cpu_id has its info in kstat
551 		 */
552 
553 		ksp = kstat_lookup (kc, "cpu_info", cpu_id, NULL);
554 		if (ksp != NULL) {
555 			iter = iter->next;
556 			continue;
557 		}
558 		/*
559 		 *  kstat info not found. Delete the device entry
560 		 */
561 		HAL_INFO ((" Remove CPU entry: %d", cpu_id));
562 		iter = iter->next;
563 		cpu_devfs_path = hal_device_property_get_string (d,
564 		    "solaris.devfs_path");
565 		if (cpu_devfs_path == NULL) {
566 			HAL_INFO (("Could not get cpu_devfs_path to "
567 			    "remove for cpu_id %d", cpu_id));
568 		} else {
569 			/*
570 			 * Remove the cpu device
571 			 */
572 			HAL_DEBUG (("Queue %s for removal", cpu_devfs_path));
573 			devinfo_remove_enqueue ((char *)cpu_devfs_path, NULL);
574 			hotplug_event_process_queue ();
575 		}
576 	}
577 
578 	if (kc) {
579 		kstat_close (kc);
580 	}
581 }
582 
583 int
584 sysevent_dr_insert_cpu(di_node_t node, void *arg)
585 {
586 	char	*devfs_path;
587 	char	*device_type = NULL;
588 	DevinfoDevHandler *dh;
589 
590 	dh = &devinfo_cpu_handler;
591 	devfs_path = di_devfs_path (node);
592 
593 	(void) di_prop_lookup_strings (DDI_DEV_T_ANY, node, "device_type",
594 	    &device_type);
595 
596 	dh->add (NULL, node, devfs_path, device_type);
597 
598 	di_devfs_path_free (devfs_path);
599 	return (DI_WALK_CONTINUE);
600 }
601 
602 /*
603  * Remove/Add the DR event device
604  * Note: Currently it supports only CPU DR events
605  */
606 static void
607 sysevent_process_dr(gchar *ap_id, gchar *hint_val)
608 {
609 	cfga_err_t 		cfgerr;
610 	cfga_list_data_t 	*cfg_stat;
611 	int			nlist;
612 	char			*errstr;
613 	di_node_t		root_node;
614 
615 	if ((ap_id == NULL) || (hint_val == NULL))
616 		return;
617 	HAL_DEBUG (("sysevent_process_dr: %s", ap_id));
618 
619 	cfgerr = config_list_ext (1, (char *const *)&ap_id, &cfg_stat, &nlist,
620 	    NULL, NULL, &errstr, 0);
621 
622 	if (cfgerr != CFGA_OK) {
623 		HAL_INFO (("DR sysevent process %d config_list_ext error: %s",
624 		    ap_id, errstr));
625 		goto out;
626 	}
627 	/*
628 	 * Check if the device type is CPU
629 	 */
630 	HAL_DEBUG ((" Ap-Type: %s, State: %d", cfg_stat->ap_type,
631 	    cfg_stat->ap_r_state));
632 	if (strcmp (cfg_stat->ap_type, "CPU") == 0) {
633 		if (strcmp (hint_val, DR_HINT_REMOVE) == 0) {
634 			sysevent_dr_remove_cpu();
635 		} else if (strcmp (hint_val, DR_HINT_INSERT) == 0) {
636 			/*
637 			 * Go through the device list and add the new cpu
638 			 * entries into HAL
639 			 */
640 			if ((root_node =
641 			    di_init ("/", DINFOCPYALL)) == DI_NODE_NIL) {
642 				HAL_INFO (("di_init failed. "\
643 				    "Cannot insert CPU"));
644 				goto out;
645 			}
646 			di_walk_node (root_node, DI_WALK_CLDFIRST, NULL,
647 			    sysevent_dr_insert_cpu);
648 			di_fini (root_node);
649 			hotplug_event_process_queue ();
650 		}
651 	} else {
652 		HAL_INFO (("Not a CPU, so cannot DR"));
653 	}
654 
655 out:
656 	if (cfg_stat)
657 		free (cfg_stat);
658 	if (errstr)
659 		free (errstr);
660 }
661