1 /*
2  * Project Acrn
3  * Acrn-dm-pty
4  *
5  * Copyright (C) 2019-2022 Intel Corporation.
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  *
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <dirent.h>
19 #include <termios.h>
20 #include <stdbool.h>
21 #include <libgen.h>
22 
23 #include "log.h"
24 
25 /*
26  * Check and create the directory.
27  * To avoid symlink failure if the directory does not exist.
28  */
check_dir(const char * file)29 static int check_dir(const char *file)
30 {
31 	char *tmp, *dir;
32 
33 	tmp = strdup(file);
34 	if (!tmp) {
35 		pr_err("failed to dup file, error:%s\n", strerror(errno));
36 		return -1;
37 	}
38 
39 	dir = dirname(tmp);
40 	if (access(dir, F_OK) && mkdir(dir, 0666)) {
41 		pr_err("failed to create dir:%s, erorr:%s\n", dir, strerror(errno));
42 		free(tmp);
43 		return -1;
44 	}
45 	free(tmp);
46 	return 0;
47 }
48 
49 /*
50  * Open PTY host device to used by caller and the PTY client device for virtual
51  * UART. The pair(host/client) can work as a communication channel between
52  * the caller and virtual UART.
53  */
pty_open_virtual_uart(const char * dev_name)54 int pty_open_virtual_uart(const char *dev_name)
55 {
56 	int fd;
57 	char *client_name;
58 	struct termios attr;
59 
60 	fd = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
61 	if (fd < 0)
62 		goto open_err;
63 	if (grantpt(fd) < 0)
64 		goto pty_err;
65 	if (unlockpt(fd) < 0)
66 		goto pty_err;
67 	client_name = ptsname(fd);
68 	if (!client_name)
69 		goto pty_err;
70 	if ((unlink(dev_name) < 0) && errno != ENOENT)
71 		goto pty_err;
72 	/*
73 	 * The check_dir restriction is that only create one directory
74 	 * not support multi-level directroy.
75 	 */
76 	if (check_dir(dev_name) < 0)
77 		goto pty_err;
78 	if (symlink(client_name, dev_name) < 0)
79 		goto pty_err;
80 	if (chmod(dev_name, 0660) < 0)
81 		goto attr_err;
82 	if (tcgetattr(fd, &attr) < 0)
83 		goto attr_err;
84 	cfmakeraw(&attr);
85 	attr.c_cflag |= CLOCAL;
86 	if (tcsetattr(fd, TCSANOW, &attr) < 0)
87 		goto attr_err;
88 	return fd;
89 
90 attr_err:
91 	unlink(dev_name);
92 pty_err:
93 	close(fd);
94 open_err:
95 	return -1;
96 }
97