Happy new year
[pazpar2-moved-to-github.git] / src / pazpar2_play.c
1 /* This file is part of Pazpar2.
2    Copyright (C) Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <time.h>
25 #include <stdlib.h>
26 #include <sys/select.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netdb.h>
30 #include <unistd.h>
31 #include <string.h>
32
33 #include <yaz/options.h>
34 #include <yaz/log.h>
35 #include <yaz/xmalloc.h>
36
37 struct con {
38     int fd;
39     long long id;
40     struct con *next;
41 };
42
43
44 static int run(int verbose, FILE *inf, struct addrinfo *res)
45 {
46     long long tv_sec0 = 0;
47     long long tv_usec0 = 0;
48     struct con *cons = 0;
49
50     while (1)
51     {
52         long long tv_sec1;
53         long long tv_usec1;
54         long long id;
55         int sz, r, c;
56         char req[100];
57         char request_type[100];
58         size_t i;
59         struct con **conp;
60         c = fgetc(inf);
61         if (c == EOF)
62             break;
63
64         for (i = 0; c != '\n' && i < (sizeof(req)-2); i++)
65         {
66             req[i] = c;
67             c = fgetc(inf);
68         }
69         req[i] = 0;
70         r = sscanf(req, "%s %lld %lld %lld %d", request_type,
71                    &tv_sec1, &tv_usec1, &id, &sz);
72         if (r != 5)
73         {
74             fprintf(stderr, "bad line %s\n", req);
75             return -1;
76         }
77         if (verbose)
78             fprintf(stderr, "read line: %s\n", req);
79         if (tv_sec0)
80         {
81             struct timeval spec;
82
83             spec.tv_sec = tv_sec1 - tv_sec0;
84             if (tv_usec0 > tv_usec1)
85             {
86                 spec.tv_usec = 1000000 + tv_usec1 - tv_usec0;
87                 spec.tv_sec--;
88             }
89             else
90                 spec.tv_usec = tv_usec1 - tv_usec0;
91
92             select(0, 0, 0, 0, &spec);
93         }
94         tv_sec0 = tv_sec1;
95         tv_usec0 = tv_usec1;
96         for (conp = &cons; *conp; conp = &(*conp)->next)
97             if ((*conp)->id == id)
98                 break;
99         if (!*conp)
100         {
101             struct addrinfo *rp;
102             int r, fd = -1;
103             for (rp = res; rp; rp = rp->ai_next)
104             {
105                 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
106                 if (fd != -1)
107                     break;
108             }
109             if (fd == -1)
110             {
111                 fprintf(stderr, "socket: cannot create\n");
112                 return -1;
113             }
114             r = connect(fd, rp->ai_addr, rp->ai_addrlen);
115             if (r)
116             {
117                 fprintf(stderr, "socket: cannot connect\n");
118                 return -1;
119             }
120
121             *conp = xmalloc(sizeof(**conp));
122             (*conp)->id = id;
123             (*conp)->fd = fd;
124             (*conp)->next = 0;
125         }
126         if (sz == 0)
127         {
128             struct con *c = *conp;
129             *conp = c->next;
130             close(c->fd);
131             xfree(c);
132         }
133         else
134         {
135             size_t cnt = 0;
136             while (cnt < sz)
137             {
138                 char buf[1024];
139                 ssize_t w;
140                 size_t r;
141                 size_t toread = sz - cnt;
142
143                 if (toread > sizeof(buf))
144                     toread = sizeof(buf);
145                 r = fread(buf, 1, toread, inf);
146                 if (r != toread)
147                 {
148                     fprintf(stderr, "fread truncated. toread=%lld r=%lld\n",
149                             (long long) toread, (long long) r);
150                     return -1;
151                 }
152                 if (verbose)
153                 {
154                     fprintf(stderr, "read %ld bytes\n---\n", (long) r);
155                     fwrite(buf, 1, r, stderr);
156                     fprintf(stderr, "\n----\n");
157                 }
158                 if (*request_type == 'r')
159                 {   /* Only deal with things that Pazpar2 received */
160                     w = write((*conp)->fd, buf, toread);
161                     if (w != toread)
162                     {
163                         fprintf(stderr, "write truncated\n");
164                         return -1;
165                     }
166                 }
167                 cnt += toread;
168             }
169         }
170     }
171     return 0;
172 }
173
174 static void usage(void)
175 {
176     fprintf(stderr, "Usage: pazpar2_play infile host\n"
177             "    -v level                Set log level\n");
178     exit(1);
179 }
180
181 int main(int argc, char **argv)
182 {
183     int ret;
184     char *arg;
185     char *host = 0;
186     int verbose = 0;
187     const char *file = 0;
188     while ((ret = options("v:", argv, argc, &arg)) != -2)
189     {
190         switch (ret)
191         {
192         case 'v':
193             yaz_log_init_level(yaz_log_mask_str(arg));
194             break;
195         case 0:
196             if (!file)
197                 file = arg;
198             else if (!host)
199                 host = xstrdup(arg);
200             else
201             {
202                 usage();
203             }
204             break;
205         default:
206             usage();
207             exit(1);
208         }
209     }
210     if (host && file)
211     {
212         char *port;
213         char *cp;
214         FILE *inf;
215
216         struct addrinfo hints, *res;
217         hints.ai_flags = 0;
218         hints.ai_family = AF_UNSPEC;
219         hints.ai_socktype = SOCK_STREAM;
220         hints.ai_protocol = 0;
221         hints.ai_addrlen        = 0;
222         hints.ai_addr           = NULL;
223         hints.ai_canonname      = NULL;
224         hints.ai_next           = NULL;
225
226         cp = strchr(host, ':');
227         if (*cp)
228         {
229             *cp = 0;
230             port = cp+1;
231         }
232         else
233         {
234             port = "80";
235         }
236         if (getaddrinfo(host, port, &hints, &res))
237         {
238             fprintf(stderr, "cannot resolve %s:%s\n", host, port);
239             exit(1);
240         }
241
242         inf = fopen(file, "rb");
243         if (!inf)
244         {
245             fprintf(stderr, "cannot open %s\n", file);
246             exit(1);
247         }
248         run(verbose, inf, res);
249         fclose(inf);
250     }
251     else
252         usage();
253     return 0;
254 }
255
256
257 /*
258  * Local variables:
259  * c-basic-offset: 4
260  * c-file-style: "Stroustrup"
261  * indent-tabs-mode: nil
262  * End:
263  * vim: shiftwidth=4 tabstop=8 expandtab
264  */
265