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 <sys/socket.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37 #include <unistd.h>
38
39 #include <ipmitool/ipmi.h>
40 #include <ipmitool/ipmi_intf.h>
41 #include <ipmitool/helper.h>
42 #include <ipmitool/log.h>
43
44 #include "dummy.h"
45
46 #if defined(HAVE_CONFIG_H)
47 # include <config.h>
48 #endif
49
50 extern int verbose;
51
52 /* data_read - read data from socket
53 *
54 * @data_ptr - pointer to memory where to store read data
55 * @data_len - how much to read from socket
56 *
57 * return 0 on success, otherwise (-1)
58 */
59 int
data_read(int fd,void * data_ptr,int data_len)60 data_read(int fd, void *data_ptr, int data_len)
61 {
62 int rc = 0;
63 int data_read = 0;
64 int data_total = 0;
65 int try = 1;
66 int errno_save = 0;
67 if (data_len < 0) {
68 return (-1);
69 }
70 while (data_total < data_len && try < 4) {
71 errno = 0;
72 /* TODO - add poll() */
73 data_read = read(fd, data_ptr, data_len);
74 errno_save = errno;
75 if (data_read > 0) {
76 data_total+= data_read;
77 }
78 if (errno_save != 0) {
79 if (errno_save == EINTR || errno_save == EAGAIN) {
80 try++;
81 sleep(2);
82 continue;
83 } else {
84 errno = errno_save;
85 perror("dummy failed on read(): ");
86 rc = (-1);
87 break;
88 }
89 }
90 }
91 if (try > 3 && data_total != data_len) {
92 rc = (-1);
93 }
94 return rc;
95 }
96
97 /* data_write - write data to the socket
98 *
99 * @data_ptr - ptr to data to send
100 * @data_len - how long is the data to send
101 *
102 * returns 0 on success, otherwise (-1)
103 */
104 int
data_write(int fd,void * data_ptr,int data_len)105 data_write(int fd, void *data_ptr, int data_len)
106 {
107 int rc = 0;
108 int data_written = 0;
109 int data_total = 0;
110 int try = 1;
111 int errno_save = 0;
112 if (data_len < 0) {
113 return (-1);
114 }
115 while (data_total < data_len && try < 4) {
116 errno = 0;
117 /* TODO - add poll() */
118 data_written = write(fd, data_ptr, data_len);
119 errno_save = errno;
120 if (data_written > 0) {
121 data_total+= data_written;
122 }
123 if (errno_save != 0) {
124 if (errno_save == EINTR || errno_save == EAGAIN) {
125 try++;
126 sleep(2);
127 continue;
128 } else {
129 errno = errno_save;
130 perror("dummy failed on read(): ");
131 rc = (-1);
132 break;
133 }
134 }
135 }
136 if (try > 3 && data_total != data_len) {
137 rc = (-1);
138 }
139 return rc;
140 }
141
142 /* ipmi_dummyipmi_close - send "BYE" and close socket
143 *
144 * @intf - ptr to initialize ipmi_intf struct
145 *
146 * returns void
147 */
148 static void
ipmi_dummyipmi_close(struct ipmi_intf * intf)149 ipmi_dummyipmi_close(struct ipmi_intf *intf)
150 {
151 struct dummy_rq req;
152 if (intf->fd < 0) {
153 return;
154 }
155 memset(&req, 0, sizeof(req));
156 req.msg.netfn = 0x3f;
157 req.msg.cmd = 0xff;
158 if (data_write(intf->fd, &req, sizeof(req)) != 0) {
159 lprintf(LOG_ERR, "dummy failed to send 'BYE'");
160 }
161 close(intf->fd);
162 intf->fd = (-1);
163 intf->opened = 0;
164 }
165
166 /* ipmi_dummyipmi_open - open socket and prepare ipmi_intf struct
167 *
168 * @intf - ptr to ipmi_inf struct
169 *
170 * returns 0 on success, (-1) on error
171 */
172 static int
ipmi_dummyipmi_open(struct ipmi_intf * intf)173 ipmi_dummyipmi_open(struct ipmi_intf *intf)
174 {
175 struct sockaddr_un address;
176 int len;
177 int rc;
178
179 if (intf->opened == 1) {
180 return intf->fd;
181 }
182 intf->fd = socket(AF_UNIX, SOCK_STREAM, 0);
183 if (intf->fd == (-1)) {
184 lprintf(LOG_ERR, "dummy failed on socket()");
185 return (-1);
186 }
187 address.sun_family = AF_UNIX;
188 strcpy(address.sun_path, DUMMY_SOCKET_PATH);
189 len = sizeof(address);
190 rc = connect(intf->fd, (struct sockaddr *)&address, len);
191 if (rc != 0) {
192 perror("dummy failed on connect(): ");
193 return (-1);
194 }
195 intf->opened = 1;
196 return intf->fd;
197 }
198
199 /* ipmi_dummyipmi_send_cmd - send IPMI payload and await reply
200 *
201 * @intf - ptr to initialized ipmi_intf struct
202 * @req - ptr to ipmi_rq struct to send
203 *
204 * return pointer to struct ipmi_rs OR NULL on error
205 */
206 static struct ipmi_rs*
ipmi_dummyipmi_send_cmd(struct ipmi_intf * intf,struct ipmi_rq * req)207 ipmi_dummyipmi_send_cmd(struct ipmi_intf *intf, struct ipmi_rq *req)
208 {
209 static struct ipmi_rs rsp;
210 struct dummy_rq req_dummy;
211 struct dummy_rs rsp_dummy;
212 if (intf == NULL || intf->fd < 0 || intf->opened != 1) {
213 lprintf(LOG_ERR, "dummy failed on intf check.");
214 return NULL;
215 }
216
217 memset(&req_dummy, 0, sizeof(req_dummy));
218 req_dummy.msg.netfn = req->msg.netfn;
219 req_dummy.msg.lun = req->msg.lun;
220 req_dummy.msg.cmd = req->msg.cmd;
221 req_dummy.msg.target_cmd = req->msg.target_cmd;
222 req_dummy.msg.data_len = req->msg.data_len;
223 req_dummy.msg.data = req->msg.data;
224 if (verbose) {
225 lprintf(LOG_NOTICE, ">>> IPMI req");
226 lprintf(LOG_NOTICE, "msg.data_len: %i",
227 req_dummy.msg.data_len);
228 lprintf(LOG_NOTICE, "msg.netfn: %x", req_dummy.msg.netfn);
229 lprintf(LOG_NOTICE, "msg.cmd: %x", req_dummy.msg.cmd);
230 lprintf(LOG_NOTICE, "msg.target_cmd: %x",
231 req_dummy.msg.target_cmd);
232 lprintf(LOG_NOTICE, "msg.lun: %x", req_dummy.msg.lun);
233 lprintf(LOG_NOTICE, ">>>");
234 }
235 if (data_write(intf->fd, &req_dummy,
236 sizeof(struct dummy_rq)) != 0) {
237 return NULL;
238 }
239 if (req->msg.data_len > 0) {
240 if (data_write(intf->fd, (uint8_t *)(req->msg.data),
241 req_dummy.msg.data_len) != 0) {
242 return NULL;
243 }
244 }
245
246 memset(&rsp_dummy, 0, sizeof(rsp_dummy));
247 if (data_read(intf->fd, &rsp_dummy, sizeof(struct dummy_rs)) != 0) {
248 return NULL;
249 }
250 if (rsp_dummy.data_len > 0) {
251 if (data_read(intf->fd, (uint8_t *)&rsp.data,
252 rsp_dummy.data_len) != 0) {
253 return NULL;
254 }
255 }
256 rsp.ccode = rsp_dummy.ccode;
257 rsp.data_len = rsp_dummy.data_len;
258 rsp.msg.netfn = rsp_dummy.msg.netfn;
259 rsp.msg.cmd = rsp_dummy.msg.cmd;
260 rsp.msg.seq = rsp_dummy.msg.seq;
261 rsp.msg.lun = rsp_dummy.msg.lun;
262 if (verbose) {
263 lprintf(LOG_NOTICE, "<<< IPMI rsp");
264 lprintf(LOG_NOTICE, "ccode: %x", rsp.ccode);
265 lprintf(LOG_NOTICE, "data_len: %i", rsp.data_len);
266 lprintf(LOG_NOTICE, "msg.netfn: %x", rsp.msg.netfn);
267 lprintf(LOG_NOTICE, "msg.cmd: %x", rsp.msg.cmd);
268 lprintf(LOG_NOTICE, "msg.seq: %x", rsp.msg.seq);
269 lprintf(LOG_NOTICE, "msg.lun: %x", rsp.msg.lun);
270 lprintf(LOG_NOTICE, "<<<");
271 }
272 return &rsp;
273 }
274
275 struct ipmi_intf ipmi_dummy_intf = {
276 .name = "dummy",
277 .desc = "Linux DummyIPMI Interface",
278 .open = ipmi_dummyipmi_open,
279 .close = ipmi_dummyipmi_close,
280 .sendrecv = ipmi_dummyipmi_send_cmd,
281 .my_addr = IPMI_BMC_SLAVE_ADDR,
282 .target_addr = IPMI_BMC_SLAVE_ADDR,
283 };
284