1 /* Copyright (c) 2013 Zdenek Styblik, All Rights Reserved
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  *
7  * Redistribution of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  *
10  * Redistribution in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * Neither the name of Zdenek Styblik or the names of
15  * contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * This software is provided "AS IS," without a warranty of any kind.
19  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
20  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
21  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
22  * Zdenek Styblik SHALL NOT BE LIABLE
23  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
24  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
25  * Zdenek Styblik BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
26  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
27  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
28  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
29  * EVEN IF Zdenek Styblik HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
30  */
31 #include <errno.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/socket.h>
36 #include <sys/types.h>
37 #include <sys/un.h>
38 #include <unistd.h>
39 
40 #include <ipmitool/ipmi.h>
41 #include <ipmitool/ipmi_intf.h>
42 #include <ipmitool/helper.h>
43 #include <ipmitool/log.h>
44 
45 #include "dummy.h"
46 
47 #if defined(HAVE_CONFIG_H)
48 # include <config.h>
49 #endif
50 
51 extern int verbose;
52 
53 /* data_read - read data from socket
54  *
55  * @data_ptr - pointer to memory where to store read data
56  * @data_len - how much to read from socket
57  *
58  * return 0 on success, otherwise (-1)
59  */
60 int
data_read(int fd,void * data_ptr,int data_len)61 data_read(int fd, void *data_ptr, int data_len)
62 {
63 	int rc = 0;
64 	int data_read = 0;
65 	int data_total = 0;
66 	int try = 1;
67 	int errno_save = 0;
68 	if (data_len < 0) {
69 		return (-1);
70 	}
71 	while (data_total < data_len && try < 4) {
72 		errno = 0;
73 		/* TODO - add poll() */
74 		data_read = read(fd, data_ptr, data_len);
75 		errno_save = errno;
76 		if (data_read > 0) {
77 			data_total+= data_read;
78 		}
79 		if (errno_save != 0) {
80 			if (errno_save == EINTR || errno_save == EAGAIN) {
81 				try++;
82 				sleep(2);
83 				continue;
84 			} else {
85 				errno = errno_save;
86 				perror("dummy failed on read(): ");
87 				rc = (-1);
88 				break;
89 			}
90 		}
91 	}
92 	if (try > 3 && data_total != data_len) {
93 		rc = (-1);
94 	}
95 	return rc;
96 }
97 
98 /* data_write - write data to the socket
99  *
100  * @data_ptr - ptr to data to send
101  * @data_len - how long is the data to send
102  *
103  * returns 0 on success, otherwise (-1)
104  */
105 int
data_write(int fd,void * data_ptr,int data_len)106 data_write(int fd, void *data_ptr, int data_len)
107 {
108 	int rc = 0;
109 	int data_written = 0;
110 	int data_total = 0;
111 	int try = 1;
112 	int errno_save = 0;
113 	if (data_len < 0) {
114 		return (-1);
115 	}
116 	while (data_total < data_len && try < 4) {
117 		errno = 0;
118 		/* TODO - add poll() */
119 		data_written = write(fd, data_ptr, data_len);
120 		errno_save = errno;
121 		if (data_written > 0) {
122 			data_total+= data_written;
123 		}
124 		if (errno_save != 0) {
125 			if (errno_save == EINTR || errno_save == EAGAIN) {
126 				try++;
127 				sleep(2);
128 				continue;
129 			} else {
130 				errno = errno_save;
131 				perror("dummy failed on read(): ");
132 				rc = (-1);
133 				break;
134 			}
135 		}
136 	}
137 	if (try > 3 && data_total != data_len) {
138 		rc = (-1);
139 	}
140 	return rc;
141 }
142 
143 /* ipmi_dummyipmi_close - send "BYE" and close socket
144  *
145  * @intf - ptr to initialize ipmi_intf struct
146  *
147  * returns void
148  */
149 static void
ipmi_dummyipmi_close(struct ipmi_intf * intf)150 ipmi_dummyipmi_close(struct ipmi_intf *intf)
151 {
152 	struct dummy_rq req;
153 	if (intf->fd < 0) {
154 		return;
155 	}
156 	memset(&req, 0, sizeof(req));
157 	req.msg.netfn = 0x3f;
158 	req.msg.cmd = 0xff;
159 	if (data_write(intf->fd, &req, sizeof(req)) != 0) {
160 		lprintf(LOG_ERR, "dummy failed to send 'BYE'");
161 	}
162 	close(intf->fd);
163 	intf->fd = (-1);
164 	intf->opened = 0;
165 }
166 
167 /* ipmi_dummyipmi_open - open socket and prepare ipmi_intf struct
168  *
169  * @intf - ptr to ipmi_inf struct
170  *
171  * returns 0 on success, (-1) on error
172  */
173 static int
ipmi_dummyipmi_open(struct ipmi_intf * intf)174 ipmi_dummyipmi_open(struct ipmi_intf *intf)
175 {
176 	struct sockaddr_un address;
177 	int len;
178 	int rc;
179 	char *dummy_sock_path;
180 
181 	dummy_sock_path = getenv("IPMI_DUMMY_SOCK");
182 	if (dummy_sock_path == NULL) {
183 		lprintf(LOG_DEBUG, "No IPMI_DUMMY_SOCK set. Dummy mode ON.");
184 		intf->opened = 1;
185 		return intf->fd;
186 	}
187 
188 	if (intf->opened == 1) {
189 		return intf->fd;
190 	}
191 	intf->fd = socket(AF_UNIX, SOCK_STREAM, 0);
192 	if (intf->fd == (-1)) {
193 		lprintf(LOG_ERR, "dummy failed on socket()");
194 		return (-1);
195 	}
196 	address.sun_family = AF_UNIX;
197 	strcpy(address.sun_path, dummy_sock_path);
198 	len = sizeof(address);
199 	rc = connect(intf->fd, (struct sockaddr *)&address, len);
200 	if (rc != 0) {
201 		perror("dummy failed on connect(): ");
202 		return (-1);
203 	}
204 	intf->opened = 1;
205 	return intf->fd;
206 }
207 
208 /* ipmi_dummyipmi_send_cmd - send IPMI payload and await reply
209  *
210  * @intf - ptr to initialized ipmi_intf struct
211  * @req - ptr to ipmi_rq struct to send
212  *
213  * return pointer to struct ipmi_rs OR NULL on error
214  */
215 static struct ipmi_rs*
ipmi_dummyipmi_send_cmd(struct ipmi_intf * intf,struct ipmi_rq * req)216 ipmi_dummyipmi_send_cmd(struct ipmi_intf *intf, struct ipmi_rq *req)
217 {
218 	static struct ipmi_rs rsp;
219 	struct dummy_rq req_dummy;
220 	struct dummy_rs rsp_dummy;
221 	char *dummy_sock_path;
222 	dummy_sock_path = getenv("IPMI_DUMMY_SOCK");
223 	if (dummy_sock_path == NULL) {
224 		lprintf(LOG_DEBUG, "No IPMI_DUMMY_SOCK set. Dummy mode ON.");
225 		return NULL;
226 	}
227 	if (intf == NULL || intf->fd < 0 || intf->opened != 1) {
228 		lprintf(LOG_ERR, "dummy failed on intf check.");
229 		return NULL;
230 	}
231 
232 	memset(&req_dummy, 0, sizeof(req_dummy));
233 	req_dummy.msg.netfn = req->msg.netfn;
234 	req_dummy.msg.lun = req->msg.lun;
235 	req_dummy.msg.cmd = req->msg.cmd;
236 	req_dummy.msg.target_cmd = req->msg.target_cmd;
237 	req_dummy.msg.data_len = req->msg.data_len;
238 	req_dummy.msg.data = req->msg.data;
239 	if (verbose) {
240 		lprintf(LOG_NOTICE, ">>> IPMI req");
241 		lprintf(LOG_NOTICE, "msg.data_len: %i",
242 				req_dummy.msg.data_len);
243 		lprintf(LOG_NOTICE, "msg.netfn: %x", req_dummy.msg.netfn);
244 		lprintf(LOG_NOTICE, "msg.cmd: %x", req_dummy.msg.cmd);
245 		lprintf(LOG_NOTICE, "msg.target_cmd: %x",
246 				req_dummy.msg.target_cmd);
247 		lprintf(LOG_NOTICE, "msg.lun: %x", req_dummy.msg.lun);
248 		lprintf(LOG_NOTICE, ">>>");
249 	}
250 	if (data_write(intf->fd, &req_dummy,
251 				sizeof(struct dummy_rq)) != 0) {
252 		return NULL;
253 	}
254 	if (req->msg.data_len > 0) {
255 		if (data_write(intf->fd, (uint8_t *)(req->msg.data),
256 					req_dummy.msg.data_len) != 0) {
257 			return NULL;
258 		}
259 	}
260 
261 	memset(&rsp_dummy, 0, sizeof(rsp_dummy));
262 	if (data_read(intf->fd, &rsp_dummy, sizeof(struct dummy_rs)) != 0) {
263 		return NULL;
264 	}
265 	if (rsp_dummy.data_len > 0) {
266 		if (data_read(intf->fd, (uint8_t *)&rsp.data,
267 					rsp_dummy.data_len) != 0) {
268 			return NULL;
269 		}
270 	}
271 	rsp.ccode = rsp_dummy.ccode;
272 	rsp.data_len = rsp_dummy.data_len;
273 	rsp.msg.netfn = rsp_dummy.msg.netfn;
274 	rsp.msg.cmd = rsp_dummy.msg.cmd;
275 	rsp.msg.seq = rsp_dummy.msg.seq;
276 	rsp.msg.lun = rsp_dummy.msg.lun;
277 	if (verbose) {
278 		lprintf(LOG_NOTICE, "<<< IPMI rsp");
279 		lprintf(LOG_NOTICE, "ccode: %x", rsp.ccode);
280 		lprintf(LOG_NOTICE, "data_len: %i", rsp.data_len);
281 		lprintf(LOG_NOTICE, "msg.netfn: %x", rsp.msg.netfn);
282 		lprintf(LOG_NOTICE, "msg.cmd: %x", rsp.msg.cmd);
283 		lprintf(LOG_NOTICE, "msg.seq: %x", rsp.msg.seq);
284 		lprintf(LOG_NOTICE, "msg.lun: %x", rsp.msg.lun);
285 		lprintf(LOG_NOTICE, "<<<");
286 	}
287 	return &rsp;
288 }
289 
290 struct ipmi_intf ipmi_dummy_intf = {
291 	.name = "dummy",
292 	.desc = "Linux DummyIPMI Interface",
293 	.open = ipmi_dummyipmi_open,
294 	.close = ipmi_dummyipmi_close,
295 	.sendrecv = ipmi_dummyipmi_send_cmd,
296 	.my_addr = IPMI_BMC_SLAVE_ADDR,
297 	.target_addr = IPMI_BMC_SLAVE_ADDR,
298 };
299