quagga: restore socket status on error paths in zclient_read function
[olsrd.git] / lib / quagga / src / client.c
1 /*
2  * OLSRd Quagga plugin
3  *
4  * Copyright (C) 2006-2008 Immo 'FaUl' Wehrenberg <immo@chaostreff-dortmund.de>
5  * Copyright (C) 2007-2012 Vasilis Tsiligiannis <acinonyxs@yahoo.gr>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation or - at your option - under
10  * the terms of the GNU General Public Licence version 2 but can be
11  * linked to any BSD-Licenced Software with public available sourcecode
12  *
13  */
14
15 /* -------------------------------------------------------------------------
16  * File               : client.c
17  * Description        : zebra client functions
18  * ------------------------------------------------------------------------- */
19
20 #define HAVE_SOCKLEN_T
21
22 #include <sys/un.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include "defs.h"
26 #include "olsr.h"
27 #include "log.h"
28 #include "routing_table.h"
29
30 #include "common.h"
31 #include "quagga.h"
32 #include "packet.h"
33 #include "client.h"
34
35 static void *my_realloc(void *, size_t, const char *);
36 static void zclient_connect(void);
37
38 static void *
39 my_realloc(void *buf, size_t s, const char *c)
40 {
41
42   buf = realloc(buf, s);
43   if (!buf) {
44     OLSR_PRINTF(1, "(QUAGGA) Out of memory: %s!\n", strerror(errno));
45     olsr_syslog(OLSR_LOG_ERR, "(QUAGGA) Out of memory!\n");
46     olsr_exit(c, EXIT_FAILURE);
47   }
48
49   return buf;
50 }
51
52 static void
53 zclient_connect(void)
54 {
55   int ret;
56
57   union {
58     struct sockaddr_in sin;
59     struct sockaddr_un sun;
60   } sockaddr;
61
62   if (close(zebra.sock) < 0)
63     olsr_exit("(QUAGGA) Could not close socket!", EXIT_FAILURE);
64   zebra.sock = socket(zebra.port ? AF_INET : AF_UNIX, SOCK_STREAM, 0);
65
66   if (zebra.sock < 0)
67     olsr_exit("(QUAGGA) Could not create socket!", EXIT_FAILURE);
68
69   memset(&sockaddr, 0, sizeof sockaddr);
70
71   if (zebra.port) {
72     sockaddr.sin.sin_family = AF_INET;
73     sockaddr.sin.sin_port = htons(zebra.port);
74     sockaddr.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
75     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sin, sizeof sockaddr.sin);
76   } else {
77     sockaddr.sun.sun_family = AF_UNIX;
78     strscpy(sockaddr.sun.sun_path, zebra.sockpath, sizeof(sockaddr.sun.sun_path));
79     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sun, sizeof sockaddr.sun);
80   }
81
82   if (ret < 0)
83     zebra.status &= ~STATUS_CONNECTED;
84   else
85     zebra.status |= STATUS_CONNECTED;
86
87 }
88
89 void
90 zclient_reconnect(void)
91 {
92   struct rt_entry *tmp;
93
94   zclient_connect();
95   if (!(zebra.status & STATUS_CONNECTED))
96     return;                     // try again next time
97
98   zebra_hello(ZEBRA_HELLO);
99   if (zebra.options & OPTION_EXPORT) {
100     OLSR_FOR_ALL_RT_ENTRIES(tmp) {
101       zebra_addroute(tmp);
102     }
103     OLSR_FOR_ALL_RT_ENTRIES_END(tmp);
104   }
105   zebra_redistribute(ZEBRA_REDISTRIBUTE_ADD);
106
107 }
108
109 int
110 zclient_write(unsigned char *options)
111 {
112   unsigned char *pnt;
113   uint16_t len;
114   int ret;
115
116   if (!(zebra.status & STATUS_CONNECTED))
117     return 0;
118
119   pnt = options;
120   memcpy(&len, pnt, sizeof len);
121
122   len = ntohs(len);
123
124   do {
125     ret = write(zebra.sock, pnt, len);
126     if (ret < 0) {
127       if ((errno == EINTR) || (errno == EAGAIN)) {
128         errno = 0;
129         ret = 0;
130         continue;
131       } else {
132         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra.\n");
133         zebra.status &= ~STATUS_CONNECTED;
134         /* TODO: Remove HNAs added from redistribution */
135         free(options);
136         return -1;
137       }
138     }
139     pnt = pnt + ret;
140   }
141   while ((len -= ret));
142   free(options);
143
144   return 0;
145 }
146
147 unsigned char *
148 zclient_read(ssize_t * size)
149 {
150   unsigned char *buf;
151   ssize_t bytes, bufsize;
152   uint16_t length, offset;
153   int sockstatus;
154
155   /* initialize variables */
156   buf = NULL;
157   offset = 0;
158   *size = 0;
159   bufsize = 0;
160
161   /* save socket status and set non-blocking for read */
162   sockstatus = fcntl(zebra.sock, F_GETFL);
163   (void)fcntl(zebra.sock, F_SETFL, sockstatus|O_NONBLOCK);
164
165   /* read whole packages */
166   do {
167
168     /* (re)allocate buffer */
169     if (*size == bufsize) {
170       ssize_t start = bufsize;
171       bufsize += BUFSIZE;
172       buf = my_realloc(buf, bufsize, "QUAGGA: Grow read buffer");
173       memset(&buf[start], 0, BUFSIZE);
174     }
175
176     /* read from socket */
177     bytes = read(zebra.sock, buf + *size, bufsize - *size);
178     /* handle broken packet */
179     if (!bytes) {
180       /* restore socket status */
181       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
182
183       free(buf);
184       return NULL;
185     }
186     /* handle no data available */
187     if (bytes < 0) {
188       /* handle disconnect */
189       if (errno != EAGAIN) {    // oops - we got disconnected
190         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra\n");
191         zebra.status &= ~STATUS_CONNECTED;
192         /* TODO: Remove HNAs added from redistribution */
193       }
194
195       /* restore socket status */
196       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
197
198       free(buf);
199       return NULL;
200     }
201
202     *size += bytes;
203
204     /* detect zebra packet fragmentation */
205     do {
206       memcpy(&length, buf + offset, sizeof(length));
207       length = ntohs(length);
208       offset += length;
209     }
210     while (*size >= (ssize_t) (offset + sizeof(length)));
211     /* set blocking socket on fragmented packet */
212     if (*size != offset)
213       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
214
215   }
216   while (*size != offset);
217
218   /* restore socket status */
219   (void)fcntl(zebra.sock, F_SETFL, sockstatus);
220
221   return buf;
222 }
223
224 /*
225  * Local Variables:
226  * c-basic-offset: 2
227  * indent-tabs-mode: nil
228  * End:
229  */