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