1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: libunimsg/sscop/sscop_main.c,v 1.5 2005/05/23 11:46:17 brandt_h Exp $
30 */
31
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/uio.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <errno.h>
39#include <unistd.h>
40#include <time.h>
41#include <signal.h>
42#include <assert.h>
43#include <err.h>
44
45#include <netnatm/unimsg.h>
46#include <netnatm/saal/sscop.h>
47#include "common.h"
48
49static int sigusr1;		/* got SIGUSR1 */
50static int unidir;		/* write only user */
51static int end_at_eof = 1;	/* send RELEASE_request at user EOF */
52
53static volatile int ready;	/* flag if connection is established */
54static volatile int finished;	/* got release confirm or indication */
55
56static const char usgtxt[] = "\
57SSCOP transport protocol\n\
58Usage: sscop [-h] [-Fbefirwx3] [-ap=v] [-lN] [-tt=m] [-v X] [-V X] [-W N]\n\
59Options:\n\
60  -F	  use framing for sscop also\n\
61  -V X	  set verbose flags to hex X\n\
62  -W N	  set initial window to N\n\
63  -a p=v  set parameter 'p' to 'v'\n\
64  -b	  enable robustness enhancement\n\
65  -e	  don't RELEASE_request on user EOF\n\
66  -f	  use begemot frame functions for user fd\n\
67  -h	  print this info\n\
68  -i	  use user fd only for output\n\
69  -lN	  loose every nth message\n\
70  -r	  reverse user and sscop file descriptors\n\
71  -t t=m  set timer 't' to 'm' milliseconds\n\
72  -v X	  set sscop verbose flags to hex X\n\
73  -w	  don't start conversation\n\
74  -x	  enable POLL after retransmission\n\
75  -3	  redirect output to fd 3\n\
76Timers are cc, poll, ka, nr or idle; parameters are j, k, cc, pd or stat.\n";
77
78static void sscop_send_manage(struct sscop *, void *,
79	enum sscop_maasig, struct uni_msg *, u_int, u_int);
80static void sscop_send_upper(struct sscop *, void *, enum sscop_aasig,
81	struct SSCOP_MBUF_T *, u_int);
82static void sscop_send_lower(struct sscop *, void *, struct SSCOP_MBUF_T *);
83
84static const struct sscop_funcs sscop_funcs = {
85	sscop_send_manage,
86	sscop_send_upper,
87	sscop_send_lower,
88	sscop_verbose,
89	sscop_start_timer,
90	sscop_stop_timer
91};
92
93/*
94 * SSCOP file descriptor is ready. Allocate and read one message
95 * and dispatch a signal.
96 */
97#ifdef USE_LIBBEGEMOT
98static void
99proto_infunc(int fd, int mask __unused, void *uap)
100#else
101static void
102proto_infunc(evContext ctx __unused, void *uap, int fd, int mask __unused)
103#endif
104{
105	struct uni_msg *m;
106
107	if ((m = proto_msgin(fd)) != NULL)
108		sscop_input((struct sscop *)uap, m);
109}
110
111/*
112 * User input. Allocate and read message and dispatch signal.
113 */
114#ifdef USE_LIBBEGEMOT
115static void
116user_infunc(int fd, int mask __unused, void *uap)
117#else
118static void
119user_infunc(evContext ctx __unused, void *uap, int fd, int mask __unused)
120#endif
121{
122	struct uni_msg *m;
123
124	if ((m = user_msgin(fd)) != NULL)
125		sscop_aasig((struct sscop *)uap, SSCOP_DATA_request, m, 0);
126
127	else if (end_at_eof)
128		sscop_aasig((struct sscop *)uap, SSCOP_RELEASE_request, 0, 0);
129}
130
131static void
132onusr1(int s __unused)
133{
134	sigusr1++;
135}
136
137int
138main(int argc, char *argv[])
139{
140	int opt;
141	struct sscop *sscop;
142	struct sscop_param param;
143	struct sigaction sa;
144	int wait = 0;
145	u_int mask;
146#ifndef USE_LIBBEGEMOT
147	evEvent ev;
148#endif
149
150	/*
151	 * Default is to have the USER on stdin and SSCOP on stdout
152	 */
153	sscop_fd = 0;
154	user_fd = 1;
155	user_out_fd = -1;
156
157	memset(&param, 0, sizeof(param));
158	param.maxk = MAXUSRMSG;
159	param.maxj = 0;
160	param.maxcc = 4;
161	mask = SSCOP_SET_MAXK | SSCOP_SET_MAXJ | SSCOP_SET_MAXCC;
162
163	while((opt = getopt(argc, argv, "3a:befFhil:rt:v:V:wW:x")) != -1)
164		switch(opt) {
165
166		  case '3':
167			user_out_fd = 3;
168			break;
169
170		  case 'e':
171			end_at_eof = 0;
172			break;
173
174		  case 'f':
175			useframe = 1;
176			break;
177
178		  case 'F':
179			sscopframe = 1;
180			break;
181
182		  case 'h':
183			fprintf(stderr, usgtxt);
184			exit(0);
185
186		  case 'i':
187			unidir++;
188			break;
189
190		  case 'l':
191			loose = strtoul(optarg, NULL, 0);
192			break;
193
194		  case 'r':
195			sscop_fd = 1;
196			user_fd = 0;
197			break;
198
199		  case 'v':
200			sscop_vflag = strtoul(optarg, NULL, 16);
201			break;
202
203		  case 'V':
204			verbose = strtoul(optarg, NULL, 16);
205			break;
206
207		  case 'w':
208			wait = 1;
209			break;
210
211		  case 'a':
212		  case 't':
213		  case 'b':
214		  case 'x':
215		  case 'W':
216			parse_param(&param, &mask, opt, optarg);
217			break;
218		}
219
220	if(user_out_fd < 0)
221		user_out_fd = user_fd;
222
223#ifndef USE_LIBBEGEMOT
224	if (evCreate(&evctx))
225		err(1, "evCreate");
226#endif
227
228	/*
229	 * Catch USR1
230	 */
231	sa.sa_handler = onusr1;
232	sigemptyset(&sa.sa_mask);
233	sa.sa_flags = SA_RESTART;
234	if(sigaction(SIGUSR1, &sa, NULL))
235		err(1, "sigaction(SIGUSR1)");
236
237	/*
238	 * Allocate and initialize SSCOP
239	 */
240	if ((sscop = sscop_create(NULL, &sscop_funcs)) == NULL)
241		err(1, NULL);
242	sscop_setdebug(sscop, sscop_vflag);
243	if ((errno = sscop_setparam(sscop, &param, &mask)) != 0)
244		err(1, "can't set sscop parameters %#x", mask);
245
246	/*
247	 * Register sscop fd
248	 */
249#ifdef USE_LIBBEGEMOT
250	if ((sscop_h = poll_register(sscop_fd, proto_infunc,
251	    sscop, POLL_IN)) == -1)
252		err(1, "can't select on sscop fd");
253#else
254	if (evSelectFD(evctx, sscop_fd, EV_READ, proto_infunc, sscop, &sscop_h))
255		err(1, "can't select on sscop fd");
256#endif
257
258	/*
259	 * if we are active - send establish request
260	 */
261	if(!wait)
262		sscop_aasig(sscop, SSCOP_ESTABLISH_request, NULL, 1);
263
264	/*
265	 * Run protocol until it get's ready
266	 */
267	while (sscop_fd >= 0 && !ready) {
268#ifdef USE_LIBBEGEMOT
269		poll_dispatch(1);
270#else
271		if (evGetNext(evctx, &ev, EV_WAIT) == 0) {
272			if (evDispatch(evctx, ev))
273				err(1, "dispatch event");
274		} else if (errno != EINTR)
275			err(1, "get event");
276#endif
277	}
278
279	/*
280	 * If this led to a closed file - exit.
281	 */
282	if (sscop_fd < 0) {
283		VERBOSE(("SSCOP file descriptor closed - exiting"));
284		sscop_destroy(sscop);
285		return 0;
286	}
287
288	VERBOSE(("READY - starting data transfer"));
289
290	if (!unidir &&
291#ifdef USE_LIBBEGEMOT
292	    ((user_h = poll_register(user_fd, user_infunc, sscop, POLL_IN)) == -1))
293#else
294	    evSelectFD(evctx, user_fd, EV_READ, user_infunc, sscop, &user_h))
295#endif
296		err(1, "can't select on sscop fd");
297
298	while (!sigusr1 && sscop_fd >= 0) {
299#ifdef USE_LIBBEGEMOT
300		poll_dispatch(1);
301#else
302		if (evGetNext(evctx, &ev, EV_WAIT) == 0) {
303			if (evDispatch(evctx, ev))
304				err(1, "dispatch event");
305		} else if (errno != EINTR)
306			err(1, "get event");
307#endif
308	}
309
310	if (sigusr1 && sscop_fd >= 0) {
311		/*
312		 * Release if we still have the connection
313		 */
314		sscop_aasig(sscop, SSCOP_RELEASE_request, NULL, 0);
315		while (!finished && sscop_fd >= 0) {
316#ifdef USE_LIBBEGEMOT
317			poll_dispatch(1);
318#else
319			if (evGetNext(evctx, &ev, EV_WAIT) == 0) {
320				if (evDispatch(evctx, ev))
321					err(1, "dispatch event");
322			} else if (errno != EINTR)
323				err(1, "get event");
324#endif
325		}
326	}
327
328	VERBOSE(("SSCOP file descriptor closed - exiting"));
329	sscop_destroy(sscop);
330
331	return (0);
332}
333
334
335
336/*
337 * AAL OUTPUT
338 */
339static void
340sscop_send_lower(struct sscop *sscop __unused, void *arg __unused,
341    struct SSCOP_MBUF_T *m)
342{
343	proto_msgout(m);
344}
345
346
347/*
348 * Write the message to the user and move the window
349 */
350static void
351uoutput(struct sscop *sscop, struct uni_msg *m)
352{
353	user_msgout(m);
354	sscop_window(sscop, +1);
355}
356
357/*
358 * SSCOP AA-SIGNALS
359 */
360static void
361sscop_send_upper(struct sscop *sscop, void *arg __unused, enum sscop_aasig sig,
362    struct SSCOP_MBUF_T *m, u_int p __unused)
363{
364	VERBOSE(("--> got aa %d(%s)", sig, sscop_signame(sig)));
365
366	switch (sig) {
367
368	  case SSCOP_RELEASE_indication:
369		if (end_at_eof) {
370			VERBOSE((" ... exiting"));
371#ifdef USE_LIBBEGEMOT
372			poll_unregister(sscop_h);
373#else
374			evDeselectFD(evctx, sscop_h);
375#endif
376			(void)close(sscop_fd);
377			sscop_fd = -1;
378		}
379		finished++;
380		if (m)
381			uni_msg_destroy(m);
382		break;
383
384	  case SSCOP_RELEASE_confirm:
385		if (end_at_eof) {
386			VERBOSE((" ... exiting"));
387#ifdef USE_LIBBEGEMOT
388			poll_unregister(sscop_h);
389#else
390			evDeselectFD(evctx, sscop_h);
391#endif
392			(void)close(sscop_fd);
393			sscop_fd = -1;
394		}
395		finished++;
396		break;
397
398	  case SSCOP_ESTABLISH_indication:
399		sscop_aasig(sscop, SSCOP_ESTABLISH_response, NULL, 1);
400		ready++;
401		if (m)
402			uni_msg_destroy(m);
403		break;
404
405	  case SSCOP_ESTABLISH_confirm:
406		ready++;
407		if (m)
408			uni_msg_destroy(m);
409		break;
410
411	  case SSCOP_DATA_indication:
412		assert(m != NULL);
413		uoutput(sscop, m);
414		break;
415
416	  case SSCOP_UDATA_indication:
417		assert(m != NULL);
418		VERBOSE(("UDATA.indication ignored"));
419		uni_msg_destroy(m);
420		break;
421
422	  case SSCOP_RECOVER_indication:
423		sscop_aasig(sscop, SSCOP_RECOVER_response, NULL, 0);
424		break;
425
426	  case SSCOP_RESYNC_indication:
427		sscop_aasig(sscop, SSCOP_RESYNC_response, NULL, 0);
428		if (m)
429			uni_msg_destroy(m);
430		break;
431
432	  case SSCOP_RESYNC_confirm:
433		break;
434
435	  case SSCOP_RETRIEVE_indication:
436	  case SSCOP_RETRIEVE_COMPL_indication:
437		warnx("Ooops. A retrieve indication");
438		abort();
439
440	  case SSCOP_ESTABLISH_request:
441	  case SSCOP_RELEASE_request:
442	  case SSCOP_ESTABLISH_response:
443	  case SSCOP_DATA_request:
444	  case SSCOP_UDATA_request:
445	  case SSCOP_RECOVER_response:
446	  case SSCOP_RESYNC_request:
447	  case SSCOP_RESYNC_response:
448	  case SSCOP_RETRIEVE_request:
449		warnx("bad signal for this direction");
450		abort();
451	}
452}
453
454/*
455 * This get's called for MAAL
456 */
457static void
458sscop_send_manage(struct sscop *sscop __unused, void *arg __unused,
459    enum sscop_maasig sig, struct uni_msg *m, u_int error, u_int cnt)
460{
461	VERBOSE(("--> got maa %d(%s)", sig, sscop_msigname(sig)));
462
463	switch (sig) {
464
465	  case SSCOP_MDATA_indication:
466		VERBOSE(("MDATA.indication ignored"));
467		uni_msg_destroy(m);
468		break;
469
470	  case SSCOP_MERROR_indication:
471		VERBOSE(("MAAL-ERROR.indication '%c' %u", error, cnt));
472		break;
473
474	  case SSCOP_MDATA_request:
475		warnx("bad signal for this direction");
476		abort();
477	}
478}
479