1 #include "utils.hpp"
2 
3 #include <libpldm/base.h>
4 #include <sys/socket.h>
5 #include <sys/types.h>
6 #include <sys/un.h>
7 #include <unistd.h>
8 
9 #include <iostream>
10 
11 namespace pldm
12 {
13 namespace responder
14 {
15 namespace utils
16 {
17 int setupUnixSocket(const std::string& socketInterface)
18 {
19     int sock;
20     struct sockaddr_un addr;
21     memset(&addr, 0, sizeof(addr));
22     addr.sun_family = AF_UNIX;
23     if (strnlen(socketInterface.c_str(), sizeof(addr.sun_path)) ==
24         sizeof(addr.sun_path))
25     {
26         std::cerr << "setupUnixSocket: UNIX socket path too long" << std::endl;
27         return -1;
28     }
29 
30     strncpy(addr.sun_path, socketInterface.c_str(), sizeof(addr.sun_path) - 1);
31 
32     if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
33     {
34         std::cerr << "setupUnixSocket: socket() call failed" << std::endl;
35         return -1;
36     }
37 
38     if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
39     {
40         std::cerr << "setupUnixSocket: bind() call failed with errno " << errno
41                   << std::endl;
42         close(sock);
43         return -1;
44     }
45 
46     if (listen(sock, 1) == -1)
47     {
48         std::cerr << "setupUnixSocket: listen() call failed" << std::endl;
49         close(sock);
50         return -1;
51     }
52 
53     fd_set rfd;
54     struct timeval tv;
55     tv.tv_sec = 1;
56     tv.tv_usec = 0;
57 
58     FD_ZERO(&rfd);
59     FD_SET(sock, &rfd);
60     int nfd = sock + 1;
61     int fd = -1;
62 
63     int retval = select(nfd, &rfd, NULL, NULL, &tv);
64     if (retval < 0)
65     {
66         std::cerr << "setupUnixSocket: select call failed " << errno
67                   << std::endl;
68         close(sock);
69         return -1;
70     }
71 
72     if ((retval > 0) && (FD_ISSET(sock, &rfd)))
73     {
74         fd = accept(sock, NULL, NULL);
75         if (fd < 0)
76         {
77             std::cerr << "setupUnixSocket: accept() call failed " << errno
78                       << std::endl;
79             close(sock);
80             return -1;
81         }
82         close(sock);
83     }
84     return fd;
85 }
86 
87 int writeToUnixSocket(const int sock, const char* buf, const uint64_t blockSize)
88 {
89     uint64_t i;
90     int nwrite = 0;
91 
92     for (i = 0; i < blockSize; i = i + nwrite)
93     {
94         fd_set wfd;
95         struct timeval tv;
96         tv.tv_sec = 1;
97         tv.tv_usec = 0;
98 
99         FD_ZERO(&wfd);
100         FD_SET(sock, &wfd);
101         int nfd = sock + 1;
102 
103         int retval = select(nfd, NULL, &wfd, NULL, &tv);
104         if (retval < 0)
105         {
106             std::cerr << "writeToUnixSocket: select call failed " << errno
107                       << std::endl;
108             close(sock);
109             return -1;
110         }
111         if (retval == 0)
112         {
113             nwrite = 0;
114             continue;
115         }
116         if ((retval > 0) && (FD_ISSET(sock, &wfd)))
117         {
118             nwrite = write(sock, buf + i, blockSize - i);
119             if (nwrite < 0)
120             {
121                 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
122                 {
123                     std::cerr << "writeToUnixSocket: Write call failed with "
124                                  "EAGAIN or EWOULDBLOCK or EINTR"
125                               << std::endl;
126                     nwrite = 0;
127                     continue;
128                 }
129                 std::cerr << "writeToUnixSocket: Failed to write" << std::endl;
130                 close(sock);
131                 return -1;
132             }
133         }
134         else
135         {
136             nwrite = 0;
137         }
138     }
139     return 0;
140 }
141 
142 } // namespace utils
143 } // namespace responder
144 } // namespace pldm
145