BRL-CAD
tpkg.c
Go to the documentation of this file.
1 /* T P K G . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2006-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file libpkg/tpkg.c
21  *
22  * Relatively simple example file transfer program using libpkg,
23  * written in a ttcp style.
24  *
25  * To compile from an install:
26  * gcc -I/usr/brlcad/include -L/usr/brlcad/lib -o tpkg tpkg.c -lpkg -lbu
27  *
28  */
29 
30 #include "common.h"
31 
32 /* system headers */
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include "bio.h"
38 
39 #include "bu/file.h"
40 #include "bu/getopt.h"
41 #include "bu/log.h"
42 #include "bu/malloc.h"
43 #include "bu/str.h"
44 
45 /* interface headers */
46 #include "pkg.h"
47 
48 
49 /* used by the client to pass the dbip and opened transfer file
50  * descriptor.
51  */
52 typedef struct _my_data_ {
53  struct pkg_conn *connection;
54  const char *server;
55  int port;
56 } my_data;
57 
58 /* simple network transport protocol. connection starts with a HELO,
59  * then a variable number of GEOM/ARGS messages, then a CIAO to end.
60  */
61 #define MAGIC_ID "TPKG"
62 #define MSG_HELO 1
63 #define MSG_DATA 2
64 #define MSG_CIAO 3
65 
66 /* maximum number of digits on a port number */
67 #define MAX_DIGITS 5
68 
69 
70 /**
71  * print a usage statement when invoked with bad, help, or no arguments
72  */
73 static void
74 usage(const char *msg, const char *argv0)
75 {
76  if (msg) {
77  bu_log("%s\n", msg);
78  }
79  bu_log("Client Usage: %s [-t] [-p#] [-b#] host file\n\t-p#\tport number to send to (default 2000)\n\t-b#\tsize of the packages sent (default 2048)\n\thost\thostname or IP address of receiving server\n\tfile\tsome file to transfer\n", argv0 ? argv0 : MAGIC_ID);
80  bu_log("Server Usage: %s -r [-p#]\n\t-p#\tport number to listen on (default 2000)\n", argv0 ? argv0 : MAGIC_ID);
81 
82  bu_log("\n%s", pkg_version());
83 
84  exit(1);
85 }
86 
87 
88 /**
89  * simple "valid" port number check used by client and server
90  */
91 static void
92 validate_port(int port) {
93  if (port < 0 || port > 0xffff) {
94  bu_bomb("Invalid negative port range\n");
95  }
96 }
97 
98 
99 /**
100  * callback when a HELO message packet is received.
101  *
102  * We should not encounter this packet specifically since we listened
103  * for it before beginning processing of packets as part of a simple
104  * handshake setup.
105  */
106 void
107 server_helo(struct pkg_conn *UNUSED(connection), char *buf)
108 {
109  bu_log("Unexpected HELO encountered\n");
110  free(buf);
111 }
112 
113 
114 /**
115  * callback when a DATA message packet is received
116  */
117 void
118 server_data(struct pkg_conn *UNUSED(connection), char *buf)
119 {
120  bu_log("Received file data\n");
121  free(buf);
122 }
123 
124 
125 /**
126  * callback when a CIAO message packet is received
127  */
128 void
129 server_ciao(struct pkg_conn *UNUSED(connection), char *buf)
130 {
131  bu_log("CIAO encountered\n");
132  free(buf);
133 }
134 
135 
136 /**
137  * start up a server that listens for a single client.
138  */
139 void
140 run_server(int port) {
141  struct pkg_conn *client;
142  int netfd;
143  char portname[MAX_DIGITS + 1] = {0};
144  int pkg_result = 0;
145  char *buffer;
146 
147  /** our server callbacks for each message type */
148  struct pkg_switch callbacks[] = {
149  {MSG_HELO, server_helo, "HELO", NULL},
150  {MSG_DATA, server_data, "DATA", NULL},
151  {MSG_CIAO, server_ciao, "CIAO", NULL},
152  {0, 0, (char *)0, (void*)0}
153  };
154 
155  validate_port(port);
156 
157  /* start up the server on the given port */
158  snprintf(portname, MAX_DIGITS, "%d", port);
159  netfd = pkg_permserver(portname, "tcp", 0, 0);
160  if (netfd < 0) {
161  bu_bomb("Unable to start the server");
162  }
163 
164  /* listen for a good client indefinitely. this is a simple
165  * handshake that waits for a HELO message from the client. if it
166  * doesn't get one, the server continues to wait.
167  */
168  do {
169  client = pkg_getclient(netfd, callbacks, NULL, 0);
170  if (client == PKC_NULL) {
171  bu_log("Connection seems to be busy, waiting...\n");
172  sleep(10);
173  continue;
174  } else if (client == PKC_ERROR) {
175  bu_log("Fatal error accepting client connection.\n");
176  pkg_close(client);
177  client = PKC_NULL;
178  continue;
179  }
180 
181  /* got a connection, process it */
182  buffer = pkg_bwaitfor (MSG_HELO, client);
183  if (buffer == NULL) {
184  bu_log("Failed to process the client connection, still waiting\n");
185  pkg_close(client);
186  client = PKC_NULL;
187  } else {
188  /* validate magic header that client should have sent */
189  if (!BU_STR_EQUAL(buffer, MAGIC_ID)) {
190  bu_log("Bizarre corruption, received a HELO without at matching MAGIC ID!\n");
191  pkg_close(client);
192  client = PKC_NULL;
193  }
194  }
195  } while (client == PKC_NULL);
196 
197  /* we got a validated client, process packets from the
198  * connection. boilerplate triple-call loop.
199  */
200  bu_log("Processing data from client\n");
201  do {
202  /* process packets potentially received in a processing callback */
203  pkg_result = pkg_process(client);
204  if (pkg_result < 0) {
205  bu_log("Unable to process packets? Weird.\n");
206  } else {
207  bu_log("Processed %d packet%s\n", pkg_result, pkg_result == 1 ? "" : "s");
208  }
209 
210  /* suck in data from the network */
211  pkg_result = pkg_suckin(client);
212  if (pkg_result < 0) {
213  bu_log("Seemed to have trouble sucking in packets.\n");
214  break;
215  } else if (pkg_result == 0) {
216  bu_log("Client closed the connection.\n");
217  break;
218  }
219 
220  /* process new packets received */
221  pkg_result = pkg_process(client);
222  if (pkg_result < 0) {
223  bu_log("Unable to process packets? Weird.\n");
224  } else {
225  bu_log("Processed %d packet%s\n", pkg_result, pkg_result == 1 ? "" : "s");
226  }
227  } while (client != NULL);
228 
229  /* shut down the server, one-time use */
230  pkg_close(client);
231 }
232 
233 
234 /**
235  * start up a client that connects to the given server, and sends
236  * serialized file data.
237  */
238 void
239 run_client(const char *server, int port, const char *file, unsigned int tpkg_bufsize)
240 {
241  my_data stash;
242  char s_port[MAX_DIGITS + 1] = {0};
243  long bytes = 0;
244  FILE *fp = (FILE *)NULL;
245  char *buffer;
246 
247  buffer = (char *)bu_calloc(tpkg_bufsize, 1, "buffer allocation");
248 
249  /* make sure the file can be opened */
250  fp = fopen(file, "rb");
251  if (fp == NULL) {
252  bu_log("Unable to open %s\n", file);
253  bu_bomb("Unable to read file\n");
254  }
255 
256  /* open a connection to the server */
257  validate_port(port);
258 
259  snprintf(s_port, MAX_DIGITS, "%d", port);
260  stash.connection = pkg_open(server, s_port, "tcp", NULL, NULL, NULL, NULL);
261  if (stash.connection == PKC_ERROR) {
262  bu_log("Connection to %s, port %d, failed.\n", server, port);
263  bu_bomb("ERROR: Unable to open a connection to the server\n");
264  }
265  stash.server = server;
266  stash.port = port;
267 
268  /* let the server know we're cool. also, send the database title
269  * along with the MAGIC ident just because we can.
270  */
271  bytes = pkg_send(MSG_HELO, MAGIC_ID, strlen(MAGIC_ID) + 1, stash.connection);
272  if (bytes < 0) {
273  pkg_close(stash.connection);
274  bu_log("Connection to %s, port %d, seems faulty.\n", server, port);
275  bu_bomb("ERROR: Unable to communicate with the server\n");
276  }
277 
278  /* send the file data to the server */
279  while (!feof(fp) && !ferror(fp)) {
280  bytes = fread(buffer, 1, tpkg_bufsize, fp);
281  bu_log("Read %ld bytes from %s\n", bytes, file);
282 
283  if (bytes > 0) {
284  bytes = pkg_send(MSG_DATA, buffer, (size_t)bytes, stash.connection);
285  if (bytes < 0) {
286  pkg_close(stash.connection);
287  bu_log("Unable to successfully send data to %s, port %d.\n", stash.server, stash.port);
288  bu_free(buffer, "buffer release");
289  return;
290  }
291  }
292  }
293 
294  /* let the server know we're done. not necessary, but polite. */
295  bytes = pkg_send(MSG_CIAO, "BYE", 4, stash.connection);
296  if (bytes < 0) {
297  bu_log("Unable to cleanly disconnect from %s, port %d.\n", server, port);
298  }
299 
300  /* flush output and close */
301  pkg_close(stash.connection);
302  fclose(fp);
303  bu_free(buffer, "buffer release");
304 
305  return;
306 }
307 
308 
309 /**
310  * main application for both the client and server
311  */
312 int
313 main(int argc, char *argv[]) {
314  const char * const argv0 = argv[0];
315  int c;
316  int server = 0; /* not a server by default */
317  int port = 2000;
318  unsigned int pkg_size = 2048;
319  /* client stuff */
320  const char *server_name = NULL;
321  const char *file = NULL;
322 
323  if (argc < 2) {
324  usage("ERROR: Missing arguments\n", argv[0]);
325  }
326 
327  /* process the command-line arguments after the application name */
328  while ((c = bu_getopt(argc, argv, "tTrRp:P:hH:b:B:")) != -1) {
329  switch (c) {
330  case 't':
331  case 'T':
332  /* sending */
333  server = 0;
334  break;
335  case 'r':
336  case 'R':
337  /* receiving */
338  server = 1;
339  break;
340  case 'p':
341  case 'P':
342  port = atoi(bu_optarg);
343  break;
344  case 'b':
345  case 'B':
346  /* Package size*/
347  pkg_size=(unsigned int)atoi(bu_optarg);
348  break;
349  case 'h':
350  case 'H':
351  /* help */
352  usage(NULL, argv0);
353  break;
354  default:
355  usage("ERROR: Unknown argument\n", argv0);
356  }
357  }
358  argc -= bu_optind;
359  argv += bu_optind;
360 
361  if (server) {
362  if (argc > 0) {
363  usage("ERROR: Unexpected extra server arguments\n", argv0);
364  }
365 
366  /* ignore broken pipes */
367 #ifdef SIGPIPE
368  (void)signal(SIGPIPE, SIG_IGN);
369 #endif
370 
371  /* fire up the server */
372  bu_log("Listening on port %d\n", port);
373  run_server(port);
374 
375  return 0;
376  }
377 
378  /* prep up the client */
379  if (argc < 1) {
380  usage("ERROR: Missing hostname and file arguments\n", argv0);
381  } else if (argc < 2) {
382  usage("ERROR: Missing file argument\n", argv0);
383  } else if (argc > 2) {
384  usage("ERROR: Too many arguments provided\n", argv0);
385  }
386 
387  server_name = *argv++;
388  file = *argv++;
389 
390  /* make sure the file exists */
391  if (!bu_file_exists(file, NULL)) {
392  bu_log("File does not exist: %s\n", file);
393  bu_bomb("Need a file to transfer\n");
394  }
395 
396  /* fire up the client */
397  bu_log("Connecting to %s, port %d\n", server_name, port);
398  run_client(server_name, port, file, pkg_size);
399 
400  return 0;
401 }
402 
403 /*
404  * Local Variables:
405  * mode: C
406  * tab-width: 8
407  * indent-tabs-mode: t
408  * c-file-style: "stroustrup"
409  * End:
410  * ex: shiftwidth=4 tabstop=8
411  */
struct pkg_conn * connection
Definition: client.c:48
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
void server_helo(struct pkg_conn *connection, char *buf)
Definition: tpkg.c:107
void pkg_close(struct pkg_conn *pc)
Definition: pkg.c:800
#define MSG_DATA
Definition: tpkg.c:63
const char * server
Definition: client.c:49
int port
Definition: client.c:50
char * bu_optarg
Definition: globals.c:91
#define MAGIC_ID
Definition: tpkg.c:61
Header file for the BRL-CAD common definitions.
int bu_optind
Definition: globals.c:89
int pkg_process(struct pkg_conn *)
Definition: pkg.c:1560
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
struct pkg_conn * pkg_open(const char *host, const char *service, const char *protocol, const char *username, const char *passwd, const struct pkg_switch *switchp, pkg_errlog errlog)
void server_ciao(struct pkg_conn *connection, char *buf)
Definition: tpkg.c:129
int pkg_send(int type, const char *buf, size_t len, struct pkg_conn *pc)
Definition: pkg.c:934
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
#define PKC_NULL
Definition: pkg.h:103
int main(int argc, char *argv[])
Definition: tpkg.c:313
#define UNUSED(parameter)
Definition: common.h:239
#define MAX_DIGITS
Definition: tpkg.c:67
void run_server(int port)
Definition: tpkg.c:140
struct _my_data_ my_data
Definition: pkg.h:57
const char * pkg_version(void)
Definition: vers.c:31
void server_data(struct pkg_conn *connection, char *buf)
Definition: tpkg.c:118
#define MSG_CIAO
Definition: tpkg.c:64
char * pkg_bwaitfor(int type, struct pkg_conn *pc)
Definition: pkg.c:1448
int pkg_permserver(const char *service, const char *protocol, int backlog, pkg_errlog)
Definition: pkg.h:79
int pkg_suckin(struct pkg_conn *)
Definition: pkg.c:1715
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
void run_client(const char *server, int port, const char *file, unsigned int tpkg_bufsize)
Definition: tpkg.c:239
#define MSG_HELO
Definition: tpkg.c:62
struct pkg_conn * pkg_getclient(int fd, const struct pkg_switch *switchp, pkg_errlog errlog, int nodelay)
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
int bu_file_exists(const char *path, int *fd)
Definition: file.c:57
#define PKC_ERROR
Definition: pkg.h:104
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126