/ src / emc / usr_intf / sockets.c
sockets.c
  1  /********************************************************************
  2  * Description: sockets.c
  3  *   socket utilites
  4  *
  5  * Copyright(c) 2001, Joris Robijn
  6  *          (c) 2003, Rene Wagner
  7  * Adapted for EMC by: Eric H. Johnson
  8  * License: GPL Version 2
  9  * System: Linux
 10  *
 11  * Copyright (c) 2007 All rights reserved.
 12  *
 13  * Last change:
 14  ********************************************************************/
 15  
 16  #include "config.h"
 17  #include <unistd.h>
 18  #include <stddef.h>
 19  #include <stdio.h>
 20  #include <string.h>
 21  #include <errno.h>
 22  #include <stdlib.h>
 23  #include <sys/types.h>
 24  
 25  #ifndef WINSOCK2
 26  #include <sys/socket.h>
 27  #include <sys/un.h>
 28  #include <netinet/in.h>
 29  #include <netdb.h>
 30  #include <arpa/inet.h>
 31  #else
 32  #include <winsock2.h>
 33  #endif
 34  
 35  #include <stdarg.h>
 36  #include <fcntl.h>
 37  
 38  #include "rcs_print.hh"
 39  #include "sockets.h"
 40  
 41  /**************************************************
 42  *  LCDproc client sockets code...
 43  *  Note: LCDproc error reporting was replaced
 44  *        with EMC standard debug reporting.
 45  **************************************************/
 46  
 47  // Length of longest transmission allowed at once...
 48  #define MAXMSG 8192
 49  
 50  typedef struct sockaddr_in sockaddr_in;
 51  
 52  static int sockInitSockaddr(sockaddr_in *name, const char *hostname, unsigned short int port)
 53  {
 54    struct hostent *hostinfo;
 55  
 56    memset(name, '\0', sizeof (*name));
 57    name->sin_family = AF_INET;
 58    name->sin_port = htons(port);
 59    hostinfo = gethostbyname(hostname);
 60    if (hostinfo == NULL) {
 61      rcs_print_error("sock_init_sockaddr: Unknown host\n");
 62      return -1;
 63      }
 64    name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
 65  
 66    return 0;
 67  }
 68  
 69  // Client functions...
 70  int sockConnect(char *host, unsigned short int port)
 71  {
 72    struct sockaddr_in servername;
 73    int sock;
 74    int err = 0;
 75  
 76    rcs_print_error("sock_connect: Creating socket\n");
 77    sock = socket(PF_INET, SOCK_STREAM, 0);
 78  #ifdef WINSOCK2        
 79    if (sock == INVALID_SOCKET) {
 80  #else
 81    if (sock < 0) {
 82  #endif
 83      rcs_print_error("sock_connect: Error creating socket\n");
 84      return sock;
 85      }
 86    rcs_print_error("sock_connect: Created socket\n");
 87  
 88    if (sockInitSockaddr(&servername, host, port) < 0)
 89      return -1;
 90  
 91    err = connect(sock, (struct sockaddr *) &servername, sizeof (servername));
 92  #ifdef WINSOCK2        
 93    if (err == INVALID_SOCKET) {
 94  #else
 95    if (err < 0) {
 96  #endif
 97      rcs_print_error("sock_connect: connect failed\n");
 98      shutdown(sock, SHUT_RDWR);
 99      return -1;
100      }
101  
102  #ifndef WINSOCK2        
103    fcntl(sock, F_SETFL, O_NONBLOCK);
104  #else
105    {
106      unsigned long tmp = 1;
107      if (ioctlsocket(sock, FIONBIO, &tmp) == SOCKET_ERROR)
108        rcs_print_error("sock_connect: Error setting socket to non-blocking\n");
109    }
110  #endif
111  
112    return sock;
113  }
114  
115  int sockClose(int fd)
116  {
117    int err;
118  
119    err = shutdown(fd, SHUT_RDWR);
120    if (!err) close (fd);
121  
122    return err;
123  }
124  
125  
126  /**  send printf-like formatted output */
127  int sockPrintf(int fd, const char *format, .../*args*/ )
128  {
129    char buf[MAXMSG];
130    va_list ap;
131    int size = 0;
132  
133    va_start(ap, format);
134    size = vsnprintf(buf, sizeof(buf), format, ap);
135    va_end(ap);
136  
137    if (size < 0) {
138      rcs_print_error("sock_printf: vsnprintf failed\n");
139      return -1;
140      }
141    if (size > sizeof(buf)) {
142      rcs_print_error("sock_printf: vsnprintf truncated message\n");
143      }
144    return sockSendString(fd, buf);
145  }
146  
147  // Send/receive lines of text
148  int sockSendString(int fd, const char *string)
149  {
150    return sockSend(fd, string, strlen(string));
151  }
152  
153  // Recv gives only one line per call...
154  int sockRecvString(int fd, char *dest, size_t maxlen)
155  {
156    char *ptr = dest;
157    int recvBytes = 0;
158  
159    if (!dest) return -1;
160    if (maxlen <= 0) return 0;
161  
162    while (1) {
163      int err = recv(fd, ptr, 1, 0);
164      if (err == -1) {
165        if (errno == EAGAIN) {
166          if (recvBytes) {
167            // We've begun to read a string, but no bytes are
168            // available.  Loop.
169            continue;
170            }
171          return 0;
172          } 
173        else {
174          rcs_print_error("sock_recv_string: socket read error");
175          return err;
176          }
177        } 
178      else if (err == 0) {
179        return recvBytes;
180        }
181  
182      recvBytes++;
183  
184      // stop at max. bytes allowed, at NUL or at LF
185      if (recvBytes == maxlen || *ptr == '\0' || *ptr == '\n') {
186        *ptr = '\0';
187        break;
188        }
189      ptr++;
190      }
191  
192    // Don't return an empty string
193    if (recvBytes == 1 && dest[0] == '\0')
194      return 0;
195  
196    if (recvBytes < maxlen - 1)
197      dest[recvBytes] = '\0';
198  
199    return recvBytes;
200  }
201  
202  // Send/receive raw data
203  int sockSend(int fd, const void *src, size_t size)
204  {
205    int offset = 0;
206  
207    if (!src) return -1;
208  
209    while (offset != size) {
210      // write isn't guaranteed to send the entire string at once,
211      // so we have to sent it in a loop like this
212  #ifndef WINSOCK2
213      int sent = write(fd, ((const char *) src) + offset, size - offset);
214  #else
215      int sent = send(fd, ((const char *) src) + offset, size - offset, 0);
216  #endif
217      if (sent == -1) {
218        if (errno != EAGAIN) {
219          rcs_print_error("sock_send: socket write error\n");
220  //      shutdown(fd, SHUT_RDWR);
221          return sent;
222          }
223        continue;
224        } 
225      else if (sent == 0) return sent + offset;
226      offset += sent;
227      } // while
228  
229    return offset;
230  }
231  
232  int sockRecv(int fd, void *dest, size_t maxlen)
233  {
234    int err;
235  
236    if (!dest) return -1;
237    if (maxlen <= 0) return 0;
238  
239  #ifndef WINSOCK2
240    err = read (fd, dest, maxlen);
241  #else
242    err = recv(fd, dest, maxlen, 0);
243  #endif
244    if (err < 0) {
245  //  rcs_print_error("sock_recv: socket read error\n");
246  //  shutdown(fd, SHUT_RDWR);
247      return err;
248      }
249  
250    return err;
251  }
252  
253  /*****************************************************************************/
254  
255  char* sockGetError(void)
256  {
257  #ifndef WINSOCK2
258    return strerror(errno);
259  #else
260    static char retString[256];
261    long err;
262    char* tmp;
263  
264    err = WSAGetLastError();
265  
266    sprintf(retString, "Error code %ld: ", err);
267    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
268                  FORMAT_MESSAGE_FROM_SYSTEM | 
269                  FORMAT_MESSAGE_IGNORE_INSERTS,
270                  NULL,
271                  err,
272                  0, /* Default language */
273                  (LPTSTR) &tmp,
274                  0,
275                  NULL);
276  
277      /* append the message text after the error code and ensure a terminating
278         character ends the string */
279    strncpy(retString + strlen(retString), tmp, 
280            sizeof(retString) - strlen(retString) - 1);
281    retString[sizeof(retString) - 1] = '\0';
282  
283    return retString;
284  #endif
285  }
286  
287  /** prints error to logfile and sends it to the client.
288   * @param fd socket
289   * @param message the message to send (without the "huh? ") */
290  int sockSendError(int fd, const char* message)
291  {
292  // simple: performance penalty isn't worth more work...
293    return sockPrintfError(fd, "%s", message);
294  }
295  
296  /** prints printf-like formatted output to logfile and sends it to the
297   * client.
298   * @note don't add a the "huh? " to the message. This is done by this
299   *   method
300   * @param fd socket
301   * @param format a printf format */
302  int sockPrintfError(int fd, const char *format, .../*args*/ )
303  {
304    static const char huh[] = "huh? ";
305    char buf[MAXMSG];
306    va_list ap;
307    int size = 0;
308  
309    strncpy(buf, huh, sizeof(huh)); // note: sizeof(huh) < MAXMSG
310  
311    va_start(ap, format);
312    size = vsnprintf(buf + (sizeof(huh)-1), sizeof(buf) - (sizeof(huh)-1), format, ap);
313    buf[sizeof(buf)-1] = '\0';
314    va_end(ap);
315  
316    if (size < 0) {
317      rcs_print_error("sock_printf_error: vsnprintf failed\n");
318      return -1;
319      }
320    if (size >= sizeof(buf) - (sizeof(huh)-1)) {
321      rcs_print_error("sock_printf_error: vsnprintf truncated message\n");
322      }
323  
324    return sockSendString(fd, buf);
325  }