Browse code

- added sercmd2, alternative to sercmd - besides using the binrpc2 library, it adds small improvements, most representative being int and str handling (made a bit more intuitive) and auto reconnecting after connection breaking up.

bpi authored on 14/01/2010 23:05:36
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,77 @@
1
+# $Id$
2
+COREPATH=../..
3
+include $(COREPATH)/Makefile.defs
4
+include $(COREPATH)/Makefile.targets
5
+
6
+auto_gen=
7
+NAME=sercmd2
8
+RELEASE=0.2
9
+
10
+readline_locations= /usr/include/readline/readline.h \
11
+					$(LOCALBASE)/include/readline/readline.h
12
+
13
+use_readline ?=
14
+ifneq (,$(MAKECMDGOALS))
15
+ifeq (,$(filter-out $(nodep_targets),$(MAKECMDGOALS)))
16
+#set it to empty, we don't need to detect/use it for clean, doc a.s.o
17
+override use_readline:=
18
+quiet=1
19
+endif
20
+endif #ifneq (,$(MAKECMDGOALS))
21
+
22
+
23
+# erase common DEFS (not needed)
24
+C_DEFS:=
25
+DEFS:= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' \
26
+		$(filter -D%HAVE -DARCH% -DOS% -D__CPU% -D__OS%, $(DEFS))
27
+DEFS+=-I$(COREPATH)/lib/binrpc2/
28
+LIBS:=$(filter-out -lfl  -ldl -lpthread -lssl -lcrypto, $(LIBS))
29
+SER_LIBS+=$(COREPATH)/lib/binrpc2/binrpc2
30
+
31
+
32
+ifeq ($(use_readline),)
33
+readline_path := $(shell  \
34
+						for r in $(readline_locations) ""; do \
35
+							if [ -r "$$r" ] ; then echo $$r; exit; fi;  \
36
+						done;\
37
+					)
38
+ifneq ($(readline_path),)
39
+use_readline := 1
40
+endif
41
+endif
42
+
43
+ifeq ($(use_readline),1)
44
+	DEFS+=-DUSE_READLINE
45
+	LIBS+=-lreadline -lncurses
46
+endif
47
+
48
+
49
+include $(COREPATH)/Makefile.utils
50
+
51
+ifeq (,$(quiet))
52
+ifeq ($(use_readline),1)
53
+$(info readline detected ($(readline_path)) )
54
+$(info command completion enabled)
55
+else
56
+$(info "no readline include files detected, disabling readline support")
57
+$(info "command completion disabled" )
58
+$(info "to force readline support try 'make use_readline=1'")
59
+endif
60
+endif # ifeq (,$(quiet))
61
+
62
+$(NAME).o: 
63
+
64
+.PHONY: msg
65
+msg:
66
+	@if [ "$(use_readline)" = "1" ]; then \
67
+		echo; echo "readline detected ($(readline_path)):"; \
68
+		echo "command completion enabled"; echo ; \
69
+	else \
70
+		echo ; \
71
+		echo "no readline include files detected, disabling readline support";\
72
+		echo "command completion disabled"; \
73
+		echo "(to force readline support try 'make use_readline=1')";\
74
+		echo ; \
75
+	fi
76
+
77
+modules:
0 78
new file mode 100644
... ...
@@ -0,0 +1,4 @@
1
+
2
+- help for internal commands & aliases
3
+- send timeout
4
+- reconnect?
0 5
new file mode 100644
... ...
@@ -0,0 +1,5 @@
1
+asi.uac.request 0 6 INVITE sip:bpintea@iptel.org sip:bot@borg sip:bpintea@iptel.org #16 asta.la.vista.baby sip:iptel.org "P-X: miau\r\nPP-X: meuw\r\nContact: %1a\r\nFull-Contact: %02pickard%03\r\nHalf-Contact: %1a\r\n" "" opaque
2
+
3
+asi.uac.cancel "33369:745761372"
4
+
5
+asi.uas.reply "33:44" 111 "Don't like him" as.231 "" ""
0 6
new file mode 100644
... ...
@@ -0,0 +1,37 @@
1
+/*
2
+ * $Id: license.h,v 1.1 2006/02/23 20:07:45 andrei Exp $
3
+ *
4
+ * sercmd GPL license, standard disclaimer and copyright
5
+ */
6
+
7
+#ifndef __license_h_
8
+#define __license_h_
9
+
10
+#define COPYRIGHT \
11
+"Copyright 2006 iptelorg GmbH\n\
12
+Copyright (C) 2007 iptego GmbH"
13
+
14
+#define DISCLAIMER \
15
+"This is free software with ABSOLUTELY NO WARRANTY.\n\
16
+For details type `warranty'."
17
+
18
+
19
+#define LICENSE \
20
+"    This program is free software; you can redistribute it and/or modify\n\
21
+    it under the terms of the GNU General Public License as published by\n\
22
+    the Free Software Foundation; either version 2 of the License , or\n\
23
+    (at your option) any later version.\n\
24
+\n\
25
+    This program is distributed in the hope that it will be useful,\n\
26
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
27
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
28
+    GNU General Public License for more details.\n\
29
+\n\
30
+    You should have received a copy of the GNU General Public License\n\
31
+    along with this program. If not, write to\n\
32
+\n\
33
+       The Free Software Foundation, Inc.\n\
34
+       51 Franklin Street, Fifth Floor,\n\
35
+       Boston, MA 02110-1301, USA."
36
+
37
+#endif
0 38
new file mode 100755
1 39
Binary files /dev/null and b/utils/sercmd2/sercmd2 differ
2 40
new file mode 100644
... ...
@@ -0,0 +1,1478 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2006 iptelorg GmbH
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/*
28
+ * send commands using binrpc
29
+ *
30
+ * History:
31
+ * --------
32
+ *  2006-02-14  created by andrei
33
+ *  2007        ported to libbinrpc (bpintea)
34
+ */
35
+
36
+
37
+#include <stdlib.h> /* exit, abort */
38
+#include <stdio.h>
39
+#include <string.h>
40
+#include <unistd.h>
41
+#include <errno.h>
42
+#include <ctype.h> /* isprint */
43
+#include <sys/socket.h>
44
+#include <sys/un.h> /* unix sock*/
45
+#include <netinet/in.h> /* udp sock */
46
+#include <sys/uio.h> /* writev */
47
+#include <netdb.h> /* gethostbyname */
48
+#include <time.h> /* time */
49
+#include <stdarg.h>
50
+#include <signal.h>
51
+
52
+#ifdef USE_READLINE
53
+#include <readline/readline.h>
54
+#include <readline/history.h>
55
+#endif
56
+
57
+#include "license.h"
58
+
59
+#include "../../modules/binrpc/modbrpc_defaults.h" /* default socket */
60
+#include <binrpc.h>
61
+
62
+#ifdef WITH_LOCDGRAM
63
+#define TMP_TEMPLATE	"/tmp/sercmd.usock.XXXXXX"
64
+#endif
65
+
66
+#define RX_TIMEOUT	60000000LU
67
+#define TX_TIMEOUT	1000000LU
68
+#define INDENT_STEP		2
69
+
70
+#ifndef NAME
71
+#define NAME    "sercmd2"
72
+#endif
73
+#ifndef VERSION
74
+#define VERSION "0.2"
75
+#endif
76
+
77
+#define MAX_LINE_SIZE 1024 /* for non readline mode */
78
+
79
+
80
+#ifndef UNIX_PATH_MAX
81
+#define UNIX_PATH_MAX 108
82
+#endif
83
+
84
+static char id[]="$Id$";
85
+static char version[]= NAME " " VERSION;
86
+static char compiled[]= __TIME__ " " __DATE__;
87
+static char help_msg[]="\
88
+Usage: " NAME " [options] [cmd]\n\
89
+\n\
90
+Options:\n\
91
+    -u URI      BINRPC URI where SER is listening.\n\
92
+    -f format   print the result using a formater.\n\
93
+    -i          quit after receiving SIGINT\n\
94
+    -v          Verbose       \n\
95
+    -V          Version number\n\
96
+    -h          This help message\n\
97
+\n\
98
+Format:\n\
99
+    A string containing `%v' markers that will be substituited with values \n\
100
+    read from the reply.\n\
101
+\n\
102
+URI:\n\
103
+    scheme://<name|path>[:port]   where scheme is brpc<n46l><sd>, with:\n\
104
+                                  'n' standing for network address,\n\
105
+                                  '4' standing for INET IPv4 address,\n\
106
+                                  '6' standing for INET IPv6 address,\n\
107
+                                  'l' for unix domain address,\n\
108
+                                  's' for stream connection and\n\
109
+                                  'd' for datagram communication.\n\
110
+                                  E.g.: brpcns://localhost:2048\n\
111
+                                        brpcld://tmp/ser.usock\n\
112
+\n\
113
+cmd:\n\
114
+    method  [arg1 [arg2...]]\n\
115
+arg:\n\
116
+     string or number; to force a certain interpretation, quote the strings\n\
117
+     and prefix numbers by a hash sign (hexa is expected in this case); \n\
118
+     e.g. \"1234\" or #deadbeef\n\
119
+\n\
120
+Examples:\n\
121
+        " NAME " -u brpcls:/tmp/ser_unix system.listMethods\n\
122
+        " NAME " -f \"pid: %v  desc: %v\\n\" -u brpcnd://localhost core.ps \n\
123
+        " NAME " ps  # uses default ctl socket \n\
124
+        " NAME "     # enters interactive mode on the default socket \n\
125
+        " NAME " -u brpcns://localhost # interactive mode, default port \n\
126
+";
127
+
128
+
129
+static int (*gen_cookie)() = rand;
130
+int verbose=0;
131
+struct sockaddr_un mysun;
132
+int quit; /* used only in interactive mode */
133
+int quit_on_sigint = 0; /* if true, quit on SIGINT */
134
+#ifdef WITH_LOCDGRAM
135
+char *tmppath = NULL;
136
+#endif
137
+
138
+
139
+brpc_str_t *commands = NULL;
140
+size_t cmds_nr = 0;
141
+
142
+
143
+enum TOK_TYPE {
144
+	TOK_NONE,
145
+	TOK_STR,
146
+	TOK_INT,
147
+	TOK_FLT,
148
+};
149
+
150
+typedef struct {
151
+	enum TOK_TYPE type;
152
+	union {
153
+		int i;
154
+		double d;
155
+		struct {
156
+			char *val;
157
+			size_t len;
158
+		} s;
159
+	};
160
+} tok_t;
161
+
162
+
163
+#define INT2STR_MAX_LEN  (19+1+1) /* 2^64~= 16*10^18 => 19+1 digits + \0 */
164
+
165
+/* returns a pointer to a static buffer containing l in asciiz & sets len */
166
+static inline char* int2str(unsigned int l, int* len)
167
+{
168
+	static char r[INT2STR_MAX_LEN];
169
+	int i;
170
+	
171
+	i=INT2STR_MAX_LEN-2;
172
+	r[INT2STR_MAX_LEN-1]=0; /* null terminate */
173
+	do{
174
+		r[i]=l%10+'0';
175
+		i--;
176
+		l/=10;
177
+	}while(l && (i>=0));
178
+	if (l && (i<0)){
179
+		fprintf(stderr, "BUG: int2str: overflow\n");
180
+	}
181
+	if (len) *len=(INT2STR_MAX_LEN-2)-i;
182
+	return &r[i+1];
183
+}
184
+
185
+
186
+
187
+static char* trim_ws(char* l)
188
+{
189
+	char* ret;
190
+	
191
+	for(;*l && ((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l++);
192
+	ret=l;
193
+	if (*ret==0) return ret;
194
+	for(l=l+strlen(l)-1; (l>ret) && 
195
+			((*l==' ')||(*l=='\t')||(*l=='\n')||(*l=='\r')); l--);
196
+	*(l+1)=0;
197
+	return ret;
198
+}
199
+
200
+
201
+
202
+struct cmd_alias{
203
+	char* name;
204
+	char* method;
205
+	char* format; /* reply print format */
206
+};
207
+
208
+
209
+struct sercmd_builtin{
210
+	char* name;
211
+	int (*f)(int, brpc_t*);
212
+	char* doc;
213
+};
214
+
215
+
216
+static int sercmd_help(int s, brpc_t *);
217
+static int sercmd_ver(int s, brpc_t *);
218
+static int sercmd_quit(int s, brpc_t *);
219
+static int sercmd_warranty(int s, brpc_t *);
220
+
221
+/* TODO: reme (core.)prints to (core.)echo */
222
+static struct cmd_alias cmd_aliases[]={
223
+	{	"ps",			"core.ps",				"%v\t%v\n"	},
224
+	{	"list",			"system.listMethods",	0			},
225
+	{	"ls",			"system.listMethods",	0			},
226
+	{	"server",		"core.version",			0			},
227
+	{	"serversion",	"core.version",			0			},
228
+	{	"who",			"binrpc.who",				
229
+			NULL},//"[%v] %v: %v %v -> %v %v\n"}, //TODO: fix command on module
230
+	{	"listen",		"binrpc.listen",			"[%v] %v: %v %v\n"},
231
+	{	"dns_mem_info",	"dns.mem_info",			"%v / %v\n"},
232
+	{	"dns_debug",	"dns.debug",			
233
+			"%v (%v): size=%v ref=%v expire=%vs last=%vs ago f=%v\n"},
234
+	{	"dns_debug_all",	"dns.debug_all",			
235
+			"%v (%v) [%v]: size=%v ref=%v expire=%vs last=%vs ago f=%v\n"
236
+			"\t\t%v:%v expire=%vs f=%v\n"},
237
+	{	"dst_blacklist_mem_info",	"dst_blacklist.mem_info",	"%v / %v\n"},
238
+	{	"dst_blacklist_debug",		"dst_blacklist.debug",	
239
+		"%v:%v:%v expire:%v flags: %v\n"},
240
+	{0,0,0}
241
+};
242
+
243
+
244
+static struct sercmd_builtin builtins[]={
245
+	{	"?",		sercmd_help, "help"},
246
+	{	"help",		sercmd_help, "displays help for a command"},
247
+	{	"version",	sercmd_ver,  "displays " NAME "version"},
248
+	{	"quit",		sercmd_quit, "exits " NAME },
249
+	{	"exit",		sercmd_quit, "exits " NAME },
250
+	{	"warranty",		sercmd_warranty, "displays " NAME "'s warranty info"},
251
+	{	"license",		sercmd_warranty, "displays " NAME "'s license"},
252
+	{0,0}
253
+};
254
+
255
+
256
+
257
+#ifdef USE_READLINE
258
+
259
+/* instead of rl_attempted_completion_over which is not present in
260
+   some readline emulations */
261
+static int attempted_completion_over=0; 
262
+
263
+/* commands for which we complete the params to other command names */
264
+char* complete_params[]={
265
+	"?",
266
+	"h",
267
+	"help",
268
+	"system.methodSignature",
269
+	"system.methodHelp",
270
+	0
271
+};
272
+#endif
273
+
274
+
275
+static char *format4method(char *method)
276
+{
277
+	int r;
278
+	
279
+	for (r = 0; cmd_aliases[r].name; r ++) {
280
+		if (strcmp(cmd_aliases[r].method, method) == 0)
281
+			return cmd_aliases[r].format;
282
+	}
283
+	return NULL;
284
+}
285
+
286
+/* resolves builtin aliases */
287
+static char *method4name(char *name, size_t nlen)
288
+{
289
+	int r;
290
+	
291
+	for (r = 0; cmd_aliases[r].name; r ++) {
292
+		if (nlen != strlen(cmd_aliases[r].name))
293
+			continue;
294
+		if (strncmp(cmd_aliases[r].name, name, nlen) != 0)
295
+			continue;
296
+		return cmd_aliases[r].method;
297
+	}
298
+	return NULL;
299
+}
300
+
301
+
302
+#if 0
303
+#define DEBUG_NEW_TOK
304
+#endif
305
+static tok_t *new_tok(char *str, size_t len, int force_str)
306
+{
307
+	tok_t *tok;
308
+	int i;
309
+	char save, *after, *curs, *end, code;
310
+
311
+	if (! (tok = (tok_t *)malloc(sizeof(tok_t)))) {
312
+		fprintf(stderr, "ERROR: out of memory.\n");
313
+		exit(1);
314
+	}
315
+	tok->type = TOK_NONE;
316
+
317
+#ifdef DEBUG_NEW_TOK
318
+	printf("RAW: [%d] `%.*s'\n", len, len, str);
319
+#endif
320
+
321
+	if (! force_str) {
322
+		/* check if could be a number */
323
+		for (i = 0; i < len && str[i] == '#'; i ++)
324
+			;
325
+		/* only one hash or no hash can identify a number (<>more #'es) */
326
+		if (i <= 1) {
327
+			save = str[len]; /* so that strXXX() can be used */
328
+			str[len] = 0;
329
+			after = NULL;
330
+			tok->i = (int)strtol(str + /*pass `#'*/i, &after, 
331
+					10 + (i ? 6 : 0));
332
+			if (after == str + len) { /* (full) conversion succeeded */
333
+				tok->type = TOK_INT;
334
+			} else {
335
+				tok->d = strtod(str + /*pass `#'*/i, &after);
336
+				if (after == str + len) {
337
+					tok->type = TOK_FLT;
338
+				}
339
+			}
340
+			str[len] = save;
341
+		}
342
+	}
343
+	if (tok->type == TOK_NONE) {
344
+		tok->type = TOK_STR;
345
+		/* This is nasty: parse the string again, to remove escaping slahes.
346
+		 * It could have been done in first pass, but need the line unmodified
347
+		 * (so that 2nd tries during reconnects are possible) */
348
+		if (! (tok->s.val = (char *)malloc(len * sizeof(char)))) {
349
+			fprintf(stderr, "ERROR: out of memory.\n");
350
+			exit(1);
351
+		}
352
+		memcpy(tok->s.val, str, len);
353
+
354
+		for (curs = tok->s.val, end = tok->s.val + len; curs < end; 
355
+				curs ++) {
356
+			switch (*curs) {
357
+				case '\\':
358
+					switch (curs[1]) { /* always safe: orig. line has 0-term */
359
+						case ' ':
360
+						case '\t':
361
+							if (force_str)
362
+								break;
363
+						case '\\':
364
+						case '"':
365
+							/* remove slash and copy rest */
366
+							memcpy(curs, curs + 1, end - curs - 1);
367
+							end --;
368
+							break;
369
+
370
+						do {
371
+						case 'r': code = 0x0D; break;
372
+						case 'n': code = 0x0A; break;
373
+						} while (0);
374
+							/* remove slash and replace by 0xa, 0xd */
375
+							memcpy(curs, curs + 1, end - curs - 1);
376
+							end --;
377
+							*curs = code;
378
+							break;
379
+
380
+						case '%':
381
+							/* remove slash, copy rest and go past `%' */
382
+							memcpy(curs, curs + 1, end - curs - 1);
383
+							end --;
384
+							curs ++; /* so that it won't be reanalized */
385
+							break;
386
+					}
387
+					break;
388
+
389
+				case '%':
390
+					if (end - curs < 2)
391
+						/* bogus % */
392
+						break;
393
+					save = curs[/*%*/1 + /*xx*/2];
394
+					curs[3] = 0;
395
+					code = strtol(curs + 1, &after, 16);
396
+					curs[3] = save;
397
+					if (after != curs + 3)
398
+						break; /* xy in %xy can not be a hexa */
399
+					/* all fine */
400
+					*curs = code;
401
+					memcpy(curs + 1, curs + 3, end - curs - 3);
402
+					end -= 2;
403
+					break; /* formal */
404
+			}
405
+		}
406
+		tok->s.len = end - tok->s.val;
407
+#ifdef DEBUG_NEW_TOK
408
+		printf("DISTILED: [%d] `%.*s'\n", tok->s.len, tok->s.len, tok->s.val);
409
+#endif
410
+
411
+	}
412
+
413
+	return tok;
414
+}
415
+
416
+static brpc_val_t *val4tok(tok_t *tok)
417
+{
418
+	brpc_val_t *val = NULL; /* 4gcc */
419
+	switch (tok->type) {
420
+		case TOK_NONE:
421
+			fprintf(stderr, "BUG: invalid token type (none).\n");
422
+			abort();
423
+			break;
424
+		case TOK_STR:
425
+			val = brpc_str(tok->s.val, tok->s.len);
426
+			break;
427
+		case TOK_INT:
428
+			val = brpc_int(tok->i);
429
+			break;
430
+		case TOK_FLT: /*TODO: support float*/
431
+			val = brpc_int(tok->d);
432
+			break; /*formal*/
433
+	}
434
+	if (! val)
435
+		fprintf(stderr, "ERROR: failed bo build BINRPC value: %s [%d].\n", 
436
+				brpc_strerror(), brpc_errno);
437
+	return val;
438
+}
439
+
440
+
441
+static brpc_val_t *parse_arg(char* arg)
442
+{
443
+	tok_t *tok;
444
+	brpc_val_t *val = NULL;
445
+
446
+	tok = new_tok(arg, strlen(arg), 0);
447
+	val = val4tok(tok);
448
+	free(tok);
449
+	return val;
450
+}
451
+
452
+static brpc_t *parse_cmd(char** argv, int count)
453
+{
454
+	int r;
455
+	brpc_t *req;
456
+	brpc_val_t *val;
457
+	
458
+	req = brpc_req_c(argv[0], gen_cookie());
459
+	if (! req) {
460
+		fprintf(stderr, "ERROR: failed to build request frame: %s [%d].\n",
461
+				brpc_strerror(), brpc_errno);
462
+		goto error;
463
+	}
464
+	for (r=1; r<count; r++){
465
+		if (! (val = parse_arg(argv[r]))) {
466
+			fprintf(stderr, "ERROR: failed to build BINRPC value for `%s':"
467
+					" %s [%d].\n", argv[r], brpc_strerror(), brpc_errno);
468
+			goto error;
469
+		}
470
+		if (! brpc_add_val(req, val)) {
471
+			fprintf(stderr, "ERROR: failed to add value to request: %s [%d].\n"
472
+					, brpc_strerror(), brpc_errno);
473
+			goto error;
474
+		}
475
+	}
476
+	return req;
477
+error:
478
+	if (req)
479
+		brpc_finish(req);
480
+	return NULL;
481
+}
482
+
483
+/* TODO: verbosity (hexdumps, print formating) */
484
+
485
+/* returns a malloced copy of str, with all the escapes ('\') resolved */
486
+static char* str_escape(char* str)
487
+{
488
+	char* n;
489
+	char* ret;
490
+	
491
+	ret=n=malloc(strlen(str)+1);
492
+	if (n==0)
493
+		goto end;
494
+	
495
+	for(;*str;str++){
496
+		*n=*str;
497
+		if (*str=='\\'){
498
+			switch(*(str+1)){
499
+				case 'n':
500
+					*n='\n';
501
+					str++;
502
+					break;
503
+				case 'r':
504
+					*n='\r';
505
+					str++;
506
+					break;
507
+				case 't':
508
+					*n='\t';
509
+					str++;
510
+					break;
511
+				case '\\':
512
+					str++;
513
+					break;
514
+			}
515
+		}
516
+		n++;
517
+	}
518
+	*n=*str; /* terminating 0 */
519
+end:
520
+	return ret;
521
+}
522
+
523
+
524
+static int print_fault(brpc_t *rpl)
525
+{
526
+	brpc_int_t *fault_code;
527
+	brpc_str_t *fault_reason;
528
+
529
+	if (! brpc_fault_status(rpl, &fault_code, &fault_reason)) {
530
+		fprintf(stderr, "ERROR: failed to retrieve fault's code&reason:"
531
+				" %s [%d].\n", brpc_strerror(), brpc_errno);
532
+		return -1;
533
+	}
534
+	printf("Call faulted: ");
535
+	
536
+	printf("code: ");
537
+	if (fault_code)
538
+		printf("%d", *fault_code);
539
+	else
540
+		printf("[unspecified]");
541
+	
542
+	printf("; reason: ");
543
+	if (fault_reason)
544
+		printf("%s", fault_reason->val);
545
+	else
546
+		printf("[unspecified]");
547
+	
548
+	printf("\n");
549
+	return 0;
550
+}
551
+
552
+/* prints all untill hits %v, and returns offset to post %v possition */
553
+static size_t print_till_v(const char *fmt)
554
+{
555
+	bool running = true;
556
+	bool hit_percent = false;
557
+	const char *saved = fmt;
558
+
559
+	while (running) {
560
+		switch (*fmt) {
561
+			case 0:
562
+				running = false;
563
+				continue;
564
+			case '%':
565
+				if (! hit_percent) {
566
+					hit_percent = true;
567
+					continue;
568
+				}
569
+				break;
570
+			case 'v':
571
+				if (hit_percent) {
572
+					fmt ++; /*step forward, past %v */
573
+					return fmt - saved;
574
+				}
575
+				break;
576
+			default:
577
+				if (hit_percent)
578
+					/* just a weird descriptor */
579
+					hit_percent = false;
580
+		}
581
+		if (! hit_percent)
582
+			printf("%c", *fmt);
583
+		fmt ++;
584
+	}
585
+	return fmt - saved;
586
+}
587
+
588
+static inline void print_indent(int count)
589
+{
590
+	int i;
591
+	for (i = 0; i < count; i ++)
592
+		printf(" ");
593
+}
594
+
595
+/* TODO: printing patterns for A/M/L */
596
+static int print_reply(brpc_t *rpl, const char * const fmt)
597
+{
598
+	brpc_dissect_t *diss;
599
+	const brpc_val_t *val;
600
+	int res = -1;
601
+	size_t offset = 0, limit;
602
+	int indent = 0;
603
+	char mark;
604
+	bool iterate = true;
605
+
606
+	diss = brpc_msg_dissector(rpl);
607
+	if (! diss) {
608
+		fprintf(stderr, "ERROR: failed to get BINRPC message dissector: "
609
+				"%s [%d].\n", brpc_strerror(), brpc_errno);
610
+		return -1;
611
+	}
612
+	limit = fmt ? strlen(fmt): 0;
613
+	do {
614
+		while (brpc_dissect_next(diss)) {
615
+			val = brpc_dissect_fetch(diss);
616
+			switch (brpc_val_type(val)) {
617
+				case BRPC_VAL_INT:
618
+				case BRPC_VAL_STR:
619
+				case BRPC_VAL_BIN:
620
+					if (fmt) {
621
+						offset += print_till_v(fmt + offset);
622
+						if (limit <= offset)
623
+							/* reset the fmt pattern */
624
+							offset = print_till_v(fmt);
625
+					} else {
626
+						print_indent(indent);
627
+					}
628
+					if (brpc_is_null(val))
629
+						printf("<NULL>");
630
+					switch (brpc_val_type(val)) {
631
+						case BRPC_VAL_INT:
632
+							printf("%d", brpc_int_val(val));
633
+							break;
634
+						case BRPC_VAL_STR:
635
+							printf("%s", brpc_str_val(val).val);
636
+							break;
637
+						case BRPC_VAL_BIN: 
638
+							printf("%.*s", (int)brpc_bin_val(val).len,
639
+									brpc_bin_val(val).val); 
640
+									break;
641
+						default: ; /* ignore */
642
+					}
643
+					break;
644
+				do {
645
+				case BRPC_VAL_AVP: mark = '<'; break;
646
+				case BRPC_VAL_MAP: mark = '{'; break;
647
+				case BRPC_VAL_LIST: mark = '['; break;
648
+				} while (0);
649
+					if (! fmt) {
650
+						print_indent(indent);
651
+						printf("%c\n", mark);
652
+						indent += INDENT_STEP;
653
+					}
654
+					if (! brpc_dissect_in(diss)) {
655
+						fprintf(stderr, "ERROR: failed to dissect into "
656
+								"sequence: %s [%d].\n", brpc_strerror(), 
657
+								brpc_errno);
658
+						goto err;
659
+					}
660
+					continue;
661
+				default:
662
+					fprintf(stderr, "unsupported value type %d.\n", 
663
+							brpc_val_type(val));
664
+			}
665
+			if (! fmt)
666
+				printf("\n");
667
+		}
668
+		switch (brpc_dissect_seqtype(diss)) {
669
+			case BRPC_VAL_NONE:
670
+				iterate = false;
671
+				break;
672
+			do {
673
+			case BRPC_VAL_AVP: mark = '>'; break;
674
+			case BRPC_VAL_MAP: mark = '}'; break;
675
+			case BRPC_VAL_LIST: mark = ']'; break;
676
+			} while (0);
677
+				if (! brpc_dissect_out(diss)) {
678
+					fprintf(stderr, "BUG: dissector error: %s [%d].\n", 
679
+							brpc_strerror(), brpc_errno);
680
+					goto err;
681
+				}
682
+				if (! fmt) {
683
+					indent -= INDENT_STEP;
684
+					print_indent(indent);
685
+					printf("%c\n", mark);
686
+				}
687
+				break;
688
+			default:
689
+				fprintf(stderr, "BUG: invalid type of sequence: %d.\n",
690
+						brpc_dissect_seqtype(diss));
691
+				goto err;;
692
+			
693
+		}
694
+	} while (iterate);
695
+
696
+	if (fmt)
697
+		/* flush any remainings */
698
+		print_till_v(fmt + offset);
699
+	res = 0;
700
+err:
701
+	brpc_dissect_free(diss);
702
+	return res;
703
+}
704
+
705
+static int run_binrpc_cmd(int s, brpc_t *req, char* fmt)
706
+{
707
+	int ret = -1;
708
+	const brpc_str_t *mname;
709
+	brpc_t *rpl = NULL;
710
+
711
+	if (! brpc_sendto(s, NULL, req, TX_TIMEOUT)) {
712
+		fprintf(stderr, "ERROR: failed to send request: %s [%d].\n",
713
+			brpc_strerror(), brpc_errno);
714
+		ret = -2;
715
+		goto finish;
716
+	}
717
+	rpl = brpc_recv(s, RX_TIMEOUT);
718
+	if (! rpl) {
719
+		fprintf(stderr, "ERROR: failed to receive reply: %s [%d].\n",
720
+			brpc_strerror(), brpc_errno);
721
+		ret = -2;
722
+		goto finish;
723
+	}
724
+	if (brpc_type(rpl) != BRPC_CALL_REPLY) {
725
+		fprintf(stderr, "ERROR: received non-reply answer.\n");
726
+		goto finish;
727
+	}
728
+	if (brpc_id(req) != brpc_id(rpl)) {
729
+		fprintf(stderr, "ERROR: received unrelated reply.\n");
730
+		goto finish;
731
+	}
732
+	if (brpc_is_fault(rpl)) {
733
+		ret = print_fault(rpl);
734
+	} else {
735
+		if (! fmt) {
736
+			mname = brpc_method(req);
737
+			if (! mname) {
738
+				fprintf(stderr, "BUG: failed to retrieve method name: "
739
+						"%s [%d].\n", brpc_strerror(), brpc_errno);
740
+				goto finish;
741
+			}
742
+			fmt = format4method(mname->val);
743
+		}
744
+		print_reply(rpl, fmt);
745
+		ret = 0;
746
+	}
747
+finish:
748
+	if (rpl)
749
+		brpc_finish(rpl);
750
+	return ret;
751
+}
752
+
753
+/**
754
+ * x"ab"y => x ab y
755
+ * "ab" "cd$ => ab cd
756
+ */
757
+static tok_t **tokenize_line(char *line, size_t *toks_cnt)
758
+{
759
+	int ended;
760
+	tok_t **toks = NULL;
761
+	size_t cnt = 0;
762
+	char *marker, *curs;
763
+
764
+#define APPEND_TOK(_start_, _len, _force_str) \
765
+	do { \
766
+		if (! (toks = realloc(toks, (cnt + 1) * sizeof(tok_t *)))) { \
767
+			fprintf(stderr, "ERROR: out of memory.\n"); \
768
+			exit(1); \
769
+		} \
770
+		toks[cnt ++] = new_tok(_start_, _len, _force_str); \
771
+	} while (0)
772
+
773
+#define EAT_WS(_s) \
774
+	do { \
775
+		switch (*_s) { \
776
+			case ' ': \
777
+			case '\t': \
778
+				_s ++; \
779
+				continue; \
780
+		} \
781
+		break; \
782
+	} while (1)
783
+
784
+#define SET_MARKER(_s) \
785
+	do { \
786
+		EAT_WS(_s); \
787
+		marker = _s; \
788
+	} while (0)
789
+
790
+	curs = line;
791
+	SET_MARKER(curs);
792
+	ended = 0;
793
+	//for (curs = line, SET_MARKER(curs), ended = 0; ! ended; ) {
794
+	while (! ended)
795
+		switch (*curs) {
796
+			case '"':
797
+				if (marker != curs) /* `ab"CD"ef' => marker=@a */
798
+					APPEND_TOK(marker, curs - marker, /*don't force*/0);
799
+				else
800
+					marker = curs + 1;
801
+				curs ++;
802
+				while (1) {
803
+					while (*curs != '"')
804
+						curs ++;
805
+					if (curs[-1] != '\\')
806
+						break;
807
+				}
808
+				APPEND_TOK(marker, curs - marker, /*force string*/1);
809
+				curs ++;
810
+				SET_MARKER(curs);
811
+				break;
812
+
813
+			case 0:
814
+				ended = 1;
815
+				if (! marker)
816
+					break;
817
+			case ' ':
818
+			case '\t':
819
+				APPEND_TOK(marker, curs - marker, /*don't force*/0);
820
+				curs ++;
821
+				SET_MARKER(curs);
822
+				break;
823
+
824
+			default:
825
+				curs ++;
826
+		}
827
+
828
+	*toks_cnt = cnt;
829
+	return toks;
830
+#undef APPEND_TOK
831
+#undef EAT_WS
832
+#undef SET_MARKER
833
+}
834
+
835
+static brpc_t *parse_line(char* line)
836
+{
837
+	size_t cnt, i;
838
+	tok_t **toks;
839
+	char *fix;
840
+	brpc_t *req = NULL;
841
+	brpc_val_t *val;
842
+	brpc_str_t mname;
843
+	int ret = -1;
844
+
845
+	if (! (toks = tokenize_line(line, &cnt)))
846
+		/* just spaces? */
847
+		return NULL;
848
+
849
+	if (toks[0]->type != TOK_STR) {
850
+		fprintf(stderr, "ERROR: request name must be a string (have %d).\n",
851
+				toks[0]->type);
852
+		goto end;
853
+	}
854
+	
855
+	fix = method4name(toks[0]->s.val, toks[0]->s.len);
856
+	if (fix) {
857
+		mname.val = fix;
858
+		mname.len = strlen(fix);
859
+	} else {
860
+		mname.val = toks[0]->s.val;
861
+		mname.len = toks[0]->s.len;
862
+	}
863
+	if (! (req = brpc_req(mname, gen_cookie()))) {
864
+		fprintf(stderr, "ERROR: failed to build request frame: %s [%d].\n",
865
+				brpc_strerror(), brpc_errno);
866
+		return NULL;
867
+	}
868
+
869
+	for (i = 1; i < cnt; i ++) {
870
+		if (! (val = val4tok(toks[i])))
871
+			goto end;
872
+		if (! brpc_add_val(req, val)) {
873
+			fprintf(stderr, "ERROR: failed to add BINRPC value to "
874
+					"request: %s [%d].\n", brpc_strerror(), brpc_errno);
875
+			goto end;
876
+		}
877
+	}
878
+
879
+	ret = 0;
880
+end:
881
+	for (i = 0; i < cnt; i ++) {
882
+		if (toks[i]->type == TOK_STR)
883
+			free(toks[i]->s.val);
884
+		free(toks[i]);
885
+	}
886
+	free(toks);
887
+	if ((ret < 0) && req) {
888
+		brpc_finish(req);
889
+		req = NULL;
890
+	}
891
+	return req;
892
+}
893
+
894
+
895
+/* intercept builtin commands, returns 1 if intercepted, 0 if not, <0 on error
896
+ */
897
+static int run_builtins(int s, brpc_t *req)
898
+{
899
+	int r;
900
+	int ret;
901
+	const brpc_str_t *mname;
902
+
903
+	mname = brpc_method(req);
904
+	if (! mname) {
905
+		fprintf(stderr, "BUG: failed to retrieve method name: %s [%d].\n",
906
+				brpc_strerror(), brpc_errno);
907
+		return 0; /* drop further attempts */
908
+	}
909
+	
910
+	for (r=0; builtins[r].name; r++){
911
+		if (strcmp(builtins[r].name, mname->val)==0){
912
+			ret=builtins[r].f(s, req);
913
+			return (ret<0)?ret:1;
914
+		}
915
+	}
916
+	return 0;
917
+}
918
+
919
+
920
+
921
+/* runs command from cmd */
922
+inline static int run_cmd(int s, brpc_t *req, char* format)
923
+{
924
+	int ret;
925
+
926
+	if (!(ret=run_builtins(s, req))){
927
+		ret=run_binrpc_cmd(s, req, format);
928
+	}
929
+	brpc_finish(req);
930
+	return ret;
931
+}
932
+
933
+
934
+
935
+/* runs a command represented in line */
936
+inline static int run_line(int s, char* l, char* format)
937
+{
938
+	brpc_t *req;
939
+	return ((req = parse_line(l))) ? run_cmd(s, req, format) : -1;
940
+}
941
+
942
+
943
+void free_cmds()
944
+{
945
+	int i;
946
+	if (cmds_nr == 0)
947
+		return;
948
+	for (i = 0; i < cmds_nr; i ++)
949
+		free(commands[i].val);
950
+	free(commands);
951
+	commands = NULL;
952
+	cmds_nr = 0;
953
+}
954
+
955
+static int get_sercmd_list(int s)
956
+{
957
+	BRPC_STR_STATIC_INIT(mname, "system.listMethods");
958
+	brpc_t *req = NULL, *rpl = NULL;
959
+	int ret = -1;
960
+	char *repr = NULL, *pos, *dup;
961
+	int i;
962
+	void **vals = NULL;
963
+	brpc_str_t *cmd;
964
+
965
+	/*if previously invoked, discard list */
966
+	if (commands)
967
+		free_cmds();
968
+	
969
+	if (! (req = brpc_req(mname, gen_cookie()))) {
970
+		fprintf(stderr, "ERROR: failed to build request: %s [%d].\n",
971
+				brpc_strerror(), brpc_errno);
972
+		goto end;
973
+	}
974
+	if (! brpc_sendto(s, NULL, req, TX_TIMEOUT)) {
975
+		fprintf(stderr, "ERROR: failed to send request: %s [%d].\n", 
976
+				brpc_strerror(), brpc_errno);
977
+		ret = -2;
978
+		goto end;
979
+	}
980
+	if (! (rpl = brpc_recv(s, RX_TIMEOUT))) {
981
+		fprintf(stderr, "ERROR: failed to retrieve response: %s [%d].\n",
982
+				brpc_strerror(), brpc_errno);
983
+		ret = -2;
984
+		goto end;
985
+	}
986
+	if (! (repr = brpc_repr(rpl, &cmds_nr))) {
987
+		fprintf(stderr, "ERROR: SER returned empty result (BUG?).\n");
988
+		goto end;
989
+	}
990
+	if (! (dup = (char *)malloc((cmds_nr + /*for leading `!'*/1 + 
991
+			/*0-term*/1) * sizeof(char))))
992
+		goto outofmem;
993
+	*dup = '!';
994
+	memcpy(dup + 1, repr, cmds_nr + /*0-term*/1);
995
+	free(repr);
996
+	repr = dup;
997
+	
998
+	if (! (vals = (void **)malloc(cmds_nr * sizeof(void *))))
999
+		goto outofmem;
1000
+	
1001
+	if (! brpc_dsm(rpl, repr, vals)) {
1002
+		fprintf(stderr, "ERROR: failed to unpack reply: %s [%d].\n",
1003
+				brpc_strerror(), brpc_errno);
1004
+		goto end;
1005
+	}
1006
+
1007
+	if (! (commands = (brpc_str_t *)malloc(cmds_nr * sizeof(brpc_str_t))))
1008
+		goto outofmem;
1009
+	for (pos = repr + /*skip !*/1, i = 0; *pos; pos ++, i ++) {
1010
+		switch (*pos) {
1011
+			case 0: break;
1012
+			case 's':
1013
+				cmd = (brpc_str_t *)vals[i];
1014
+				if (! cmd) {
1015
+					fprintf(stderr, "ERROR: unexpected NULL value as command"
1016
+							" name (BUG?).\n");
1017
+						goto freecmds;
1018
+				}
1019
+				commands[i].len = cmd->len;
1020
+				if (! (commands[i].val = (char *)malloc(cmd->len * 
1021
+						sizeof(char))))
1022
+					goto freecmds;
1023
+				memcpy(commands[i].val, cmd->val, cmd->len);
1024
+				printf("%s\n", cmd->val);
1025
+				break;
1026
+			default:
1027
+				fprintf(stderr, "ERROR: unexpected descriptor `%c' (0x%x) in "
1028
+						"representation `%s' (BUG?).\n", *pos, *pos, repr);
1029
+				goto end;
1030
+		}
1031
+	}
1032
+
1033
+	ret = 0;
1034
+	goto end;
1035
+freecmds:
1036
+	cmds_nr = i -1;
1037
+	if (commands) {
1038
+		free_cmds();
1039
+		commands = NULL;
1040
+	}
1041
+	goto end; //nasty
1042
+outofmem:
1043
+	fprintf(stderr, "ERROR: ouf of system memory.\n");
1044
+end:
1045
+	if (vals)
1046
+		free(vals);
1047
+	if (repr)
1048
+		free(repr);
1049
+	if (req)
1050
+		brpc_finish(req);
1051
+	if (rpl)
1052
+		brpc_finish(rpl);
1053
+	return ret;
1054
+}
1055
+
1056
+static int sercmd_help(int s, brpc_t *helper)
1057
+{
1058
+	int r;
1059
+	brpc_t *req = NULL;
1060
+	const brpc_val_t *val;
1061
+	brpc_val_t *clone;
1062
+	brpc_dissect_t *diss = NULL;
1063
+	BRPC_STR_STATIC_INIT(get_help, "system.methodHelp");
1064
+	size_t cnt;
1065
+	int ret = -1;
1066
+
1067
+	if (! (diss = brpc_msg_dissector(helper))) {
1068
+		fprintf(stderr, "ERROR: failed to dissect helper: %s [%d].\n",
1069
+				brpc_strerror(), brpc_errno);
1070
+		goto error;
1071
+	}
1072
+	if ((cnt = brpc_dissect_cnt(diss)) == 0) {
1073
+		if ((ret = get_sercmd_list(s)) < 0)
1074
+			goto error;
1075
+
1076
+		for (r = 0; r < cmds_nr; r ++)
1077
+			printf("%s\n", commands[r].val);
1078
+		for (r=0; cmd_aliases[r].name; r++){
1079
+			printf("alias: %s\n", cmd_aliases[r].name);
1080
+		}
1081
+		for(r=0; builtins[r].name; r++){
1082
+			printf("builtin: %s\n", builtins[r].name);
1083
+		}
1084
+		ret = 0;
1085
+	} else {
1086
+		if (! (req = brpc_req(get_help, gen_cookie()))) {
1087
+			fprintf(stderr, "ERROR: failed to build help request: "
1088
+					"%s [%d].\n", brpc_strerror(), brpc_errno);
1089
+			goto error;
1090
+		}
1091
+		brpc_dissect_next(diss); /*_cnt returned 1: assume OK*/
1092
+		val = brpc_dissect_fetch(diss);
1093
+		if (brpc_val_type(val) != BRPC_VAL_STR) {
1094
+			fprintf(stderr, "ERROR: invalid token for help request.\n");
1095
+			goto error;
1096
+		}
1097
+		if (1 < cnt)
1098
+			fprintf(stderr, "WARNING: multiple tokens supplied for help "
1099
+					"request; considering only first (`%s').\n", 
1100
+					brpc_str_val(val).val);
1101
+		clone = brpc_val_clone(val); /* old val is just a ref in helper */
1102
+		if (! brpc_add_val(req, clone)) {
1103
+			fprintf(stderr, "ERROR: failed to clone help token: %s [%d].\n",
1104
+					brpc_strerror(), brpc_errno);
1105
+			goto error;
1106
+		}
1107
+		ret = run_binrpc_cmd(s, req, 0);
1108
+	}
1109
+
1110
+error:
1111
+	if (diss)
1112
+		brpc_dissect_free(diss);
1113
+	return ret;
1114
+}
1115
+
1116
+
1117
+
1118
+static int sercmd_ver(int s, brpc_t *_)
1119
+{
1120
+	printf("%s\n", version);
1121
+	printf("%s\n", id);
1122
+	printf("%s compiled on %s \n", __FILE__, compiled);
1123
+#ifdef USE_READLINE
1124
+	printf("interactive mode command completion support\n");
1125
+#endif
1126
+	return 0;
1127
+}
1128
+
1129
+
1130
+
1131
+static int sercmd_quit(int s, brpc_t *_)
1132
+{
1133
+	quit=1;
1134
+	return 0;
1135
+}
1136
+
1137
+
1138
+
1139
+static int sercmd_warranty(int s, brpc_t *_)
1140
+{
1141
+	printf("%s %s\n", NAME, VERSION);
1142
+	printf("%s\n", COPYRIGHT);
1143
+	printf("\n%s\n", LICENSE);
1144
+	return 0;
1145
+}
1146
+
1147
+
1148
+#ifdef USE_READLINE
1149
+
1150
+/* readline command generator */
1151
+static char* sercmd_generator(const char* text, int state)
1152
+{
1153
+	static int idx;
1154
+	static int list; /* aliases, builtins, rpc_array */
1155
+	static int len;
1156
+	char* name;
1157
+	
1158
+	if (attempted_completion_over)
1159
+		return 0;
1160
+	
1161
+	if (state==0){
1162
+		/* init */
1163
+		idx=list=0;
1164
+		len=strlen(text);
1165
+	}
1166
+	/* return next partial match */
1167
+	switch(list){
1168
+		case 0: /* aliases*/
1169
+			while((name=cmd_aliases[idx].name)){
1170
+				idx++;
1171
+				if (strncmp(name, text, len)==0)
1172
+					return strdup(name);
1173
+			}
1174
+			list++;
1175
+			idx=0;
1176
+			/* no break */
1177
+		case 1: /* builtins */
1178
+			while((name=builtins[idx].name)){
1179
+				idx++;
1180
+				if (strncmp(name, text, len)==0)
1181
+					return strdup(name);
1182
+			}
1183
+			list++;
1184
+			idx=0;
1185
+			/* no break */
1186
+		case 2: /* rpc_array */
1187
+			while(idx < cmds_nr){
1188
+				name = commands[idx ++].val;
1189
+				if (strncmp(name, text, len) == 0)
1190
+					return strdup(name);
1191
+			}
1192
+	}
1193
+	/* no matches */
1194
+	return 0;
1195
+}
1196
+
1197
+
1198
+char** sercmd_completion(const char* text, int start, int end)
1199
+{
1200
+	int r;
1201
+	int i;
1202
+	
1203
+	attempted_completion_over=1;
1204
+	/* complete only at beginning */
1205
+	if (start==0){
1206
+		attempted_completion_over=0;
1207
+	}else{ /* or if this is a command for which we complete the parameters */
1208
+		/* find first whitespace */
1209
+		for(r=0; (r<start) && (rl_line_buffer[r]!=' ') && 
1210
+				(rl_line_buffer[r]!='\t'); r++);
1211
+		for(i=0; complete_params[i]; i++){
1212
+			if ((r==strlen(complete_params[i])) &&
1213
+					(strncmp(rl_line_buffer, complete_params[i], r)==0)){
1214
+					attempted_completion_over=0;
1215
+					break;
1216
+			}
1217
+		}
1218
+	}
1219
+	return 0; /* let readline call sercmd_generator */
1220
+}
1221
+
1222
+#endif /* USE_READLINE */
1223
+
1224
+
1225
+
1226
+void log_libbinrpc(int level, const char *fmt, ...)
1227
+{
1228
+	va_list ap;
1229
+	if ((level & /*no facility*/0x7) <= (verbose + 3 + ((3<=verbose)?1:0))) {
1230
+		va_start(ap, fmt);
1231
+		vfprintf(stderr, fmt, ap);
1232
+		va_end(ap);
1233
+	}
1234
+}
1235
+
1236
+int connect2ser(char *uri)
1237
+{
1238
+	brpc_addr_t *ser_addr;
1239
+	int s = -1;
1240
+#ifdef WITH_LOCDGRAM
1241
+	int tmpfd;
1242
+	brpc_addr_t loc_addr;
1243
+#endif
1244
+
1245
+	ser_addr = brpc_parse_uri(uri);
1246
+	if (! ser_addr) {
1247
+		fprintf(stderr, "ERROR: failed to parse URI `%s': %s [%d].\n", uri,
1248
+				brpc_strerror(), brpc_errno);
1249
+		return -1;
1250
+	}
1251
+#ifdef WITH_LOCDGRAM
1252
+	if ((BRPC_ADDR_DOMAIN(ser_addr) == PF_LOCAL) && 
1253
+			(BRPC_ADDR_TYPE(ser_addr) == SOCK_DGRAM)) {
1254
+		if (tmppath) {
1255
+			unlink(tmppath);
1256
+			free(tmppath);
1257
+		}
1258
+		if (! (tmppath = strdup(TMP_TEMPLATE))) {
1259
+			fprintf(stderr, "ERROR: out of memory [%d].\n", errno);
1260
+			return -1;
1261
+		}
1262
+		if ((tmpfd = mkstemp(tmppath)) < 0) {
1263
+			fprintf(stderr, "ERROR: failed to create local unix socket: "
1264
+					"%s [%d].\n", strerror(errno), errno);
1265
+			return -1;
1266
+		}
1267
+		close(tmpfd);
1268
+		unlink(tmppath);
1269
+		if (sizeof(BRPC_ADDR_UN(&loc_addr)->sun_path) < 
1270
+				strlen(tmppath) + /*0-term*/1) {
1271
+			fprintf(stderr, "BUG: temporary socket template too long.\n");
1272
+			return -1;
1273
+		}
1274
+		loc_addr = *ser_addr;
1275
+		memcpy(BRPC_ADDR_UN(&loc_addr)->sun_path, tmppath, 
1276
+				strlen(tmppath) + /*0-term*/1);
1277
+		BRPC_ADDR_LEN(&loc_addr) = SUN_LEN(BRPC_ADDR_UN(&loc_addr));
1278
+		if ((s = brpc_socket(&loc_addr, /*blocking*/true, /*name*/true)) < 0) {
1279
+			fprintf(stderr, "ERROR: failed to create named socket: %s [%d].\n",
1280
+					brpc_strerror(), brpc_errno);
1281
+			return -1;
1282
+		}
1283
+	}