1 /*  -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Support for the Matrox "lspci" channel
4    Copyright (C) 2005 Matrox Graphics Inc.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "rdesktop.h"
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 static VCHANNEL *lspci_channel;
26 
27 typedef struct _pci_device
28 {
29 	uint16 klass;
30 	uint16 vendor;
31 	uint16 device;
32 	uint16 subvendor;
33 	uint16 subdevice;
34 	uint8 revision;
35 	uint8 progif;
36 } pci_device;
37 
38 static pci_device current_device;
39 
40 static void lspci_send(RDPCLIENT * This, const char *output);
41 
42 
43 /* Handle one line of output from the lspci subprocess */
44 static BOOL
handle_child_line(RDPCLIENT * This,const char * line,void * data)45 handle_child_line(RDPCLIENT * This, const char *line, void *data)
46 {
47 	const char *val;
48 	char buf[1024];
49 
50 	if (str_startswith(line, "Class:"))
51 	{
52 		val = line + sizeof("Class:");
53 		/* Skip whitespace and second Class: occurance */
54 		val += strspn(val, " \t") + sizeof("Class");
55 		current_device.klass = strtol(val, NULL, 16);
56 	}
57 	else if (str_startswith(line, "Vendor:"))
58 	{
59 		val = line + sizeof("Vendor:");
60 		current_device.vendor = strtol(val, NULL, 16);
61 	}
62 	else if (str_startswith(line, "Device:"))
63 	{
64 		val = line + sizeof("Device:");
65 		/* Sigh, there are *two* lines tagged as Device:. We
66 		   are not interested in the domain/bus/slot/func */
67 		if (!strchr(val, ':'))
68 			current_device.device = strtol(val, NULL, 16);
69 	}
70 	else if (str_startswith(line, "SVendor:"))
71 	{
72 		val = line + sizeof("SVendor:");
73 		current_device.subvendor = strtol(val, NULL, 16);
74 	}
75 	else if (str_startswith(line, "SDevice:"))
76 	{
77 		val = line + sizeof("SDevice:");
78 		current_device.subdevice = strtol(val, NULL, 16);
79 	}
80 	else if (str_startswith(line, "Rev:"))
81 	{
82 		val = line + sizeof("Rev:");
83 		current_device.revision = strtol(val, NULL, 16);
84 	}
85 	else if (str_startswith(line, "ProgIf:"))
86 	{
87 		val = line + sizeof("ProgIf:");
88 		current_device.progif = strtol(val, NULL, 16);
89 	}
90 	else if (strspn(line, " \t") == strlen(line))
91 	{
92 		/* Blank line. Send collected information over channel */
93 		snprintf(buf, sizeof(buf), "%04x,%04x,%04x,%04x,%04x,%02x,%02x\n",
94 			 current_device.klass, current_device.vendor,
95 			 current_device.device, current_device.subvendor,
96 			 current_device.subdevice, current_device.revision, current_device.progif);
97 		lspci_send(This, buf);
98 		memset(&current_device, 0, sizeof(current_device));
99 	}
100 	else
101 	{
102 		warning("lspci: Unrecoqnized line '%s'\n", line);
103 	}
104 	return True;
105 }
106 
107 
108 /* Process one line of input from virtual channel */
109 static BOOL
lspci_process_line(RDPCLIENT * This,const char * line,void * data)110 lspci_process_line(RDPCLIENT * This, const char *line, void *data)
111 {
112 	char *lspci_command[5] = { "lspci", "-m", "-n", "-v", NULL };
113 
114 	if (!strcmp(line, "LSPCI"))
115 	{
116 		memset(&current_device, 0, sizeof(current_device));
117 		subprocess(This, lspci_command, handle_child_line, NULL);
118 		/* Send single dot to indicate end of enumeration */
119 		lspci_send(This, ".\n");
120 	}
121 	else
122 	{
123 		error("lspci protocol error: Invalid line '%s'\n", line);
124 	}
125 	return True;
126 }
127 
128 
129 /* Process new data from the virtual channel */
130 static void
lspci_process(RDPCLIENT * This,STREAM s)131 lspci_process(RDPCLIENT * This, STREAM s)
132 {
133 	unsigned int pkglen;
134 	static char *rest = NULL;
135 	char *buf;
136 
137 	pkglen = s->end - s->p;
138 	/* str_handle_lines requires null terminated strings */
139 	buf = xmalloc(pkglen + 1);
140 	STRNCPY(buf, (char *) s->p, pkglen + 1);
141 #if 0
142 	printf("lspci recv:\n");
143 	hexdump(s->p, pkglen);
144 #endif
145 
146 	str_handle_lines(This, buf, &rest, lspci_process_line, NULL);
147 	xfree(buf);
148 }
149 
150 /* Initialize this module: Register the lspci channel */
151 BOOL
lspci_init(RDPCLIENT * This)152 lspci_init(RDPCLIENT * This)
153 {
154 	lspci_channel =
155 		channel_register(This, "lspci", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
156 				 lspci_process);
157 	return (lspci_channel != NULL);
158 }
159 
160 /* Send data to channel */
161 static void
lspci_send(RDPCLIENT * This,const char * output)162 lspci_send(RDPCLIENT * This, const char *output)
163 {
164 	STREAM s;
165 	size_t len;
166 
167 	len = strlen(output);
168 	s = channel_init(This, lspci_channel, len);
169 	out_uint8p(s, output, len) s_mark_end(s);
170 
171 #if 0
172 	printf("lspci send:\n");
173 	hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);
174 #endif
175 
176 	channel_send(This, s, lspci_channel);
177 }
178