xref: /illumos-gate/usr/src/cmd/vntsd/vntsdvcc.c (revision 3af08d828975d7e2581b6829e0eecff14d87a483)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Configuration and setup interface to vcc driver.
29  * At intialization time, vntsd opens vcc ctrl port and read initial
30  * configuratioa. It manages console groups, creates the listen thread,
31  * dynamically adds and removes virtual console within a group.
32  */
33 
34 
35 #include <syslog.h>
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/ipc.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <sys/ipc.h>
44 #include <sys/shm.h>
45 #include <sys/sem.h>
46 #include <wait.h>
47 #include <time.h>
48 #include <synch.h>
49 #include <netinet/in.h>
50 #include <thread.h>
51 #include <signal.h>
52 #include "vntsd.h"
53 
54 /* signal all clients that console has been deleted */
55 boolean_t
56 vntsd_notify_client_cons_del(vntsd_client_t *clientp)
57 {
58 	(void) mutex_lock(&clientp->lock);
59 	clientp->status |= VNTSD_CLIENT_CONS_DELETED;
60 	(void) thr_kill(clientp->cons_tid, SIGUSR1);
61 	(void) mutex_unlock(&clientp->lock);
62 	return (B_FALSE);
63 }
64 
65 /* free console  structure */
66 static void
67 free_cons(vntsd_cons_t *consp)
68 {
69 	assert(consp);
70 	(void) mutex_destroy(&consp->lock);
71 	(void) cond_destroy(&consp->cvp);
72 	free(consp);
73 }
74 
75 /*
76  *  all clients connected to a console must disconnect before
77  *  removing a console.
78  */
79 static void
80 cleanup_cons(vntsd_cons_t *consp)
81 {
82 	vntsd_group_t	*groupp;
83 	timestruc_t	to;
84 
85 	assert(consp);
86 	D1(stderr, "t@%d vntsd_disconn_clients@%d\n", thr_self(),
87 	    consp->cons_no);
88 
89 	groupp = consp->group;
90 	assert(groupp);
91 
92 
93 	(void) mutex_lock(&consp->lock);
94 
95 	/* wait for all clients disconnect from the console */
96 	while (consp->clientpq != NULL) {
97 		consp->status |= VNTSD_CONS_SIG_WAIT;
98 
99 		/* signal client to disconnect the console */
100 		(void) vntsd_que_walk(consp->clientpq,
101 		    (el_func_t)vntsd_notify_client_cons_del);
102 
103 		(void) thr_kill(consp->wr_tid, SIGUSR1);
104 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
105 		to.tv_nsec = 0;
106 
107 		/* wait for clients to disconnect  */
108 		(void) cond_reltimedwait(&consp->cvp, &consp->lock, &to);
109 	}
110 
111 	/* reduce console count in the group */
112 	(void) mutex_lock(&groupp->lock);
113 	assert(groupp->num_cons > 0);
114 	groupp->num_cons--;
115 	(void) mutex_unlock(&groupp->lock);
116 
117 	(void) mutex_unlock(&consp->lock);
118 
119 	free_cons(consp);
120 }
121 
122 /* search for a group whose console is being deleted */
123 static boolean_t
124 find_clean_cons_group(vntsd_group_t *groupp)
125 {
126 	if (groupp->status & VNTSD_GROUP_CLEAN_CONS) {
127 		return (B_TRUE);
128 	} else {
129 		return (B_FALSE);
130 	}
131 }
132 
133 /* search for a console that is being deleted */
134 static boolean_t
135 find_clean_cons(vntsd_cons_t *consp)
136 {
137 	if (consp->status & VNTSD_CONS_DELETED) {
138 		return (B_TRUE);
139 	} else {
140 		return (B_FALSE);
141 	}
142 }
143 
144 /* delete a console */
145 void
146 vntsd_delete_cons(vntsd_t *vntsdp)
147 {
148 	vntsd_group_t *groupp;
149 	vntsd_cons_t *consp;
150 
151 	for (; ; ) {
152 		/* get the group contains deleted console */
153 		(void) mutex_lock(&vntsdp->lock);
154 		groupp = vntsd_que_walk(vntsdp->grouppq,
155 		    (el_func_t)find_clean_cons_group);
156 		if (groupp == NULL) {
157 			/* no more group has console deleted */
158 			(void) mutex_unlock(&vntsdp->lock);
159 			return;
160 		}
161 		(void) mutex_lock(&groupp->lock);
162 		groupp->status &= ~VNTSD_GROUP_CLEAN_CONS;
163 		(void) mutex_unlock(&groupp->lock);
164 		(void) mutex_unlock(&vntsdp->lock);
165 
166 		for (; ; ) {
167 			/* get the console to be deleted */
168 			(void) mutex_lock(&groupp->lock);
169 			assert(groupp->conspq);
170 			consp = vntsd_que_walk(groupp->conspq,
171 			    (el_func_t)find_clean_cons);
172 			if (consp == NULL) {
173 				/* no more cons to delete */
174 				(void) mutex_unlock(&groupp->lock);
175 				break;
176 			}
177 
178 			/* remove console from the group */
179 			(void) vntsd_que_rm(&groupp->conspq, consp);
180 			(void) mutex_unlock(&groupp->lock);
181 
182 			/* clean up the console */
183 			cleanup_cons(consp);
184 
185 			/* delete group? */
186 			if (groupp->num_cons == 0) {
187 				/* no more console delete it */
188 				assert(groupp->vntsd);
189 
190 				(void) mutex_lock(&groupp->vntsd->lock);
191 				(void) vntsd_que_rm(&groupp->vntsd->grouppq,
192 						    groupp);
193 				(void) mutex_unlock(&groupp->vntsd->lock);
194 
195 				/* clean up the group */
196 				vntsd_clean_group(groupp);
197 				break;
198 			}
199 		}
200 	}
201 }
202 
203 /* clean up a group */
204 void
205 vntsd_clean_group(vntsd_group_t *groupp)
206 {
207 
208 	timestruc_t	to;
209 
210 	D1(stderr, "t@%d clean_group() group=%s tcp=%lld\n", thr_self(),
211 	    groupp->group_name, groupp->tcp_port);
212 
213 	(void) mutex_lock(&groupp->lock);
214 
215 	/* prevent from reentry */
216 	if (groupp->status & VNTSD_GROUP_CLEANUP) {
217 		if (groupp->listen_tid == thr_self()) {
218 			/* signal that the listen thread is exiting */
219 			groupp->status &= ~VNTSD_GROUP_SIG_WAIT;
220 			(void) cond_signal(&groupp->cvp);
221 		}
222 		(void) mutex_unlock(&groupp->lock);
223 		return;
224 	}
225 	groupp->status |= VNTSD_GROUP_CLEANUP;
226 	(void) mutex_unlock(&groupp->lock);
227 
228 	vntsd_free_que(&groupp->conspq, (clean_func_t)cleanup_cons);
229 
230 	/* walk through no cons client queue */
231 	while (groupp->no_cons_clientpq != NULL) {
232 		groupp->status |= VNTSD_GROUP_SIG_WAIT;
233 		(void) vntsd_que_walk(groupp->no_cons_clientpq,
234 		    (el_func_t)vntsd_notify_client_cons_del);
235 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
236 		to.tv_nsec = 0;
237 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
238 	}
239 
240 	if (groupp->listen_tid == thr_self()) {
241 		/* listen thread is exiting */
242 		(void) mutex_lock(&(groupp->vntsd->lock));
243 		(void) vntsd_que_rm(&groupp->vntsd->grouppq, groupp);
244 		(void) mutex_unlock(&groupp->vntsd->lock);
245 
246 		(void) cond_destroy(&groupp->cvp);
247 		(void) mutex_unlock(&groupp->lock);
248 		(void) mutex_destroy(&groupp->lock);
249 		free(groupp);
250 		return;
251 	}
252 
253 	/* signal listen thread to exit  */
254 	groupp->status |= VNTSD_GROUP_SIG_WAIT;
255 
256 	while (groupp->status & VNTSD_GROUP_SIG_WAIT) {
257 		(void) thr_kill(groupp->listen_tid, SIGUSR1);
258 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
259 		to.tv_nsec = 0;
260 		/* wait listen thread to exit  */
261 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
262 	}
263 
264 	(void) mutex_unlock(&groupp->lock);
265 	(void) thr_join(groupp->listen_tid, NULL, NULL);
266 	/* free group */
267 	(void) cond_destroy(&groupp->cvp);
268 	(void) mutex_destroy(&groupp->lock);
269 	free(groupp);
270 }
271 
272 /* allocate and initialize console structure */
273 static vntsd_cons_t *
274 alloc_cons(vntsd_group_t *groupp, vcc_console_t *consolep)
275 {
276 	vntsd_cons_t *consp;
277 	int	rv;
278 
279 	/* allocate console */
280 	consp = (vntsd_cons_t *)malloc(sizeof (vntsd_cons_t));
281 	if (consp == NULL) {
282 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_cons");
283 		return (NULL);
284 	}
285 
286 	/* intialize console */
287 	bzero(consp, sizeof (vntsd_cons_t));
288 
289 	(void) mutex_init(&consp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
290 	(void) cond_init(&consp->cvp, USYNC_THREAD, NULL);
291 
292 	consp->cons_no = consolep->cons_no;
293 	(void) strlcpy(consp->domain_name, consolep->domain_name, MAXPATHLEN);
294 	(void) strlcpy(consp->dev_name, consolep->dev_name, MAXPATHLEN);
295 	consp->wr_tid = (thread_t)-1;
296 	consp->vcc_fd = (thread_t)-1;
297 
298 	/* join the group */
299 	(void) mutex_lock(&groupp->lock);
300 
301 	if ((rv = vntsd_que_append(&groupp->conspq, consp)) !=
302 	    VNTSD_SUCCESS) {
303 		(void) mutex_unlock(&groupp->lock);
304 		vntsd_log(rv, "alloc_cons");
305 		free_cons(consp);
306 		return (NULL);
307 	}
308 	groupp->num_cons++;
309 	consp->group = groupp;
310 
311 	(void) mutex_unlock(&groupp->lock);
312 
313 	D1(stderr, "t@%d alloc_cons@%d %s %s\n", thr_self(),
314 	    consp->cons_no, consp->domain_name, consp->dev_name);
315 
316 	return (consp);
317 }
318 
319 /* compare tcp with group->tcp */
320 static boolean_t
321 grp_by_tcp(vntsd_group_t *groupp, uint64_t *tcp_port)
322 {
323 	assert(groupp);
324 	assert(tcp_port);
325 	return (groupp->tcp_port == *tcp_port);
326 }
327 
328 /* allocate and initialize group */
329 static vntsd_group_t *
330 alloc_group(vntsd_t *vntsdp, char *group_name, uint64_t tcp_port)
331 {
332 	vntsd_group_t *groupp;
333 
334 	/* allocate group */
335 	groupp = (vntsd_group_t *)malloc(sizeof (vntsd_group_t));
336 	if (groupp == NULL) {
337 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_group");
338 		return (NULL);
339 	}
340 
341 	/* initialize group */
342 	bzero(groupp, sizeof (vntsd_group_t));
343 
344 	(void) mutex_init(&groupp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
345 	(void) cond_init(&groupp->cvp, USYNC_THREAD, NULL);
346 
347 	if (group_name != NULL) {
348 		(void) memcpy(groupp->group_name, group_name, MAXPATHLEN);
349 	}
350 
351 	groupp->tcp_port = tcp_port;
352 	groupp->listen_tid = (thread_t)-1;
353 	groupp->sockfd = (thread_t)-1;
354 	groupp->vntsd = vntsdp;
355 
356 	D1(stderr, "t@%d alloc_group@%lld:%s\n", thr_self(), groupp->tcp_port,
357 	    groupp->group_name);
358 
359 	return (groupp);
360 }
361 
362 /*
363  * Initialize a console, if console is associated with with a
364  * new group, intialize the group.
365  */
366 static int
367 alloc_cons_with_group(vntsd_t *vntsdp, vcc_console_t *consp,
368     vntsd_group_t **new_groupp)
369 {
370 	vntsd_group_t	*groupp = NULL;
371 	int		rv;
372 
373 	*new_groupp = NULL;
374 
375 	/* match group by tcp port */
376 
377 
378 	(void) mutex_lock(&vntsdp->lock);
379 	groupp = vntsd_que_find(vntsdp->grouppq,
380 	    (compare_func_t)grp_by_tcp, (void *)&(consp->tcp_port));
381 	(void) mutex_unlock(&vntsdp->lock);
382 
383 	if (groupp != NULL) {
384 		/* group with same tcp port found */
385 
386 		if (strcmp(groupp->group_name, consp->group_name)) {
387 			/* conflict group name */
388 			vntsd_log(VNTSD_ERR_VCC_GRP_NAME,
389 			    "group name is different from existing group");
390 			return (VNTSD_ERR_VCC_CTRL_DATA);
391 		}
392 
393 	} else {
394 		/* new group */
395 		groupp = alloc_group(vntsdp, consp->group_name,
396 		    consp->tcp_port);
397 		if (groupp == NULL) {
398 			return (VNTSD_ERR_NO_MEM);
399 		}
400 
401 		assert(groupp->conspq == NULL);
402 		/* queue group to vntsdp */
403 		(void) mutex_lock(&vntsdp->lock);
404 		rv = vntsd_que_append(&vntsdp->grouppq, groupp);
405 		(void) mutex_unlock(&vntsdp->lock);
406 
407 		if (rv != VNTSD_SUCCESS) {
408 			return (rv);
409 		}
410 
411 		*new_groupp = groupp;
412 	}
413 
414 	/* intialize console */
415 	if (alloc_cons(groupp, consp) == NULL) {
416 		/* no memory */
417 		if (new_groupp != NULL) {
418 			/* clean up new group */
419 			(void) cond_destroy(&groupp->cvp);
420 			(void) mutex_destroy(&groupp->lock);
421 			free(groupp);
422 		}
423 
424 		return (VNTSD_ERR_NO_MEM);
425 	}
426 
427 	return (VNTSD_SUCCESS);
428 
429 }
430 
431 
432 /* create listen thread */
433 static boolean_t
434 create_listen_thread(vntsd_group_t *groupp)
435 {
436 
437 	char err_msg[VNTSD_LINE_LEN];
438 	int rv;
439 
440 	assert(groupp);
441 
442 	(void) mutex_lock(&groupp->lock);
443 	assert(groupp->num_cons);
444 
445 	D1(stderr, "t@%d create_listen:%lld\n", thr_self(), groupp->tcp_port);
446 
447 	if ((rv = thr_create(NULL, 0, (thr_func_t)vntsd_listen_thread,
448 			    (void *)groupp, THR_DETACHED, &groupp->listen_tid))
449 	    != 0) {
450 		(void) (void) snprintf(err_msg, sizeof (err_msg),
451 		    "Can not create listen thread for"
452 		    "group %s tcp %llx\n", groupp->group_name,
453 		    groupp->tcp_port);
454 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
455 
456 		/* clean up group queue */
457 		vntsd_free_que(&groupp->conspq, (clean_func_t)free_cons);
458 		groupp->listen_tid = (thread_t)-1;
459 	}
460 
461 	(void) mutex_unlock(&groupp->lock);
462 
463 	return (rv != 0);
464 }
465 
466 /* delete a console if the console exists in the vntsd */
467 static void
468 delete_cons_before_add(vntsd_t *vntsdp, uint64_t tcp_port, uint_t cons_no)
469 {
470 	vntsd_group_t	    *groupp;
471 	vntsd_cons_t	    *consp;
472 
473 	/* group exists? */
474 	(void) mutex_lock(&vntsdp->lock);
475 	groupp = vntsd_que_find(vntsdp->grouppq, (compare_func_t)grp_by_tcp,
476 	    (void *)&(tcp_port));
477 	(void) mutex_unlock(&vntsdp->lock);
478 
479 	if (groupp == NULL) {
480 		/* no such group */
481 		return;
482 	}
483 
484 	/* group exists, if console exists? */
485 	(void) mutex_lock(&groupp->lock);
486 	consp = vntsd_que_find(groupp->conspq,
487 	    (compare_func_t)vntsd_cons_by_consno, &cons_no);
488 
489 	if (consp == NULL) {
490 		/* no such console */
491 		(void) mutex_unlock(&groupp->lock);
492 		return;
493 	}
494 	/* console exists - delete console */
495 
496 	(void) mutex_lock(&consp->lock);
497 
498 	consp->status |= VNTSD_CONS_DELETED;
499 	groupp->status |= VNTSD_GROUP_CLEAN_CONS;
500 
501 	(void) mutex_unlock(&consp->lock);
502 
503 	(void) mutex_unlock(&groupp->lock);
504 
505 	vntsd_delete_cons(vntsdp);
506 }
507 
508 /* add a console */
509 static void
510 do_add_cons(vntsd_t *vntsdp, int cons_no)
511 {
512 	vcc_console_t	console;
513 	vntsd_group_t	*groupp;
514 	int		rv;
515 	char		err_msg[VNTSD_LINE_LEN];
516 
517 
518 	(void) snprintf(err_msg, sizeof (err_msg),
519 	    "do_add_cons():Can not add console=%d", cons_no);
520 
521 	/* get console configuration from vcc */
522 
523 	if ((rv = vntsd_vcc_ioctl(VCC_CONS_INFO, cons_no, (void *)&console))
524 	    != VNTSD_SUCCESS) {
525 		vntsd_log(rv, err_msg);
526 		return;
527 	}
528 
529 	/* clean up the console if console was deleted and added again */
530 	delete_cons_before_add(vntsdp, console.tcp_port, console.cons_no);
531 
532 	/* initialize console */
533 
534 	if ((rv = alloc_cons_with_group(vntsdp, &console, &groupp)) !=
535 	    VNTSD_SUCCESS) {
536 		/* no memory to add this new console */
537 		vntsd_log(rv, err_msg);
538 		return;
539 	}
540 
541 	if (groupp != NULL) {
542 		/* new group */
543 		/* create listen thread for this console */
544 		if (create_listen_thread(groupp)) {
545 			vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
546 			(void) cond_destroy(&groupp->cvp);
547 			(void) mutex_destroy(&groupp->lock);
548 			free(groupp);
549 		}
550 
551 	}
552 }
553 
554 /* daemon wake up */
555 void
556 vntsd_daemon_wakeup(vntsd_t *vntsdp)
557 {
558 
559 	vcc_response_t	inq_data;
560 
561 	/* reason to wake up  */
562 	if (vntsd_vcc_ioctl(VCC_INQUIRY, 0, (void *)&inq_data) !=
563 	    VNTSD_SUCCESS) {
564 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "vntsd_daemon_wakeup()");
565 		return;
566 	}
567 
568 	D1(stderr, "t@%d vntsd_daemon_wakup:msg %d port %x\n", thr_self(),
569 	    inq_data.reason, inq_data.cons_no);
570 
571 	switch (inq_data.reason) {
572 
573 	case VCC_CONS_ADDED:
574 		do_add_cons(vntsdp, inq_data.cons_no);
575 		break;
576 
577 	case VCC_CONS_MISS_ADDED:
578 		/* an added port was deleted before vntsd can process it */
579 		return;
580 
581 	default:
582 		DERR(stderr, "t@%d daemon_wakeup:ioctl_unknown %d\n",
583 		    thr_self(), inq_data.reason);
584 		vntsd_log(VNTSD_ERR_UNKNOWN_CMD, "from vcc\n");
585 		break;
586 	}
587 }
588 
589 /* initial console configuration */
590 void
591 vntsd_get_config(vntsd_t *vntsdp)
592 {
593 
594 	int		i;
595 	int		num_cons;
596 	vcc_console_t	*consp;
597 	vntsd_group_t	*groupp;
598 
599 	/* num of consoles */
600 	num_cons = 0;
601 
602 	if (vntsd_vcc_ioctl(VCC_NUM_CONSOLE, 0, (void *)&num_cons) !=
603 	    VNTSD_SUCCESS) {
604 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "VCC_NUM_CONSOLE failed\n");
605 		return;
606 	}
607 
608 	D3(stderr, "get_config:num_cons=%d", num_cons);
609 
610 	if (num_cons == 0) {
611 		return;
612 	}
613 
614 	/* allocate memory for all consoles */
615 	consp = malloc(num_cons*sizeof (vcc_console_t));
616 
617 	if (consp == NULL) {
618 		vntsd_log(VNTSD_ERR_NO_MEM, "for console table.");
619 		return;
620 	}
621 
622 	/* get console table */
623 	if (vntsd_vcc_ioctl(VCC_CONS_TBL, 0, (void *)consp) != VNTSD_SUCCESS) {
624 		vntsd_log(VNTSD_ERR_VCC_IOCTL, " VCC_CONS_TBL "
625 		    "for console table\n");
626 		return;
627 	}
628 
629 	/* intialize groups and consoles  */
630 	for (i = 0; i < num_cons; i++) {
631 		if (alloc_cons_with_group(vntsdp, &consp[i], &groupp)
632 		    != VNTSD_SUCCESS) {
633 			vntsd_log(VNTSD_ERR_ADD_CONS_FAILED, "get_config");
634 		}
635 	}
636 
637 	/* create listen thread for each group */
638 	(void) mutex_lock(&vntsdp->lock);
639 
640 	for (; ; ) {
641 		groupp = vntsd_que_walk(vntsdp->grouppq,
642 		    (el_func_t)create_listen_thread);
643 		if (groupp == NULL) {
644 			break;
645 		}
646 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, "get config()");
647 	}
648 
649 	(void) mutex_unlock(&vntsdp->lock);
650 }
651