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