3 * Hidetoshi Shimokawa. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
16 * This product includes software developed by Hidetoshi Shimokawa.
18 * 4. Neither the name of the author nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
35 * $FreeBSD: src/usr.sbin/dconschat/dconschat.c,v 1.6 2004/05/26 22:59:54 brooks Exp $
36 * $DragonFly: src/usr.sbin/dconschat/dconschat.c,v 1.1 2004/09/23 06:38:29 simokawa Exp $
39 #include <sys/param.h>
40 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
53 #include <sys/eui64.h>
54 #include <sys/event.h>
56 #include <arpa/telnet.h>
59 #include <dev/misc/dcons/dcons.h>
60 #include <bus/firewire/firewire.h>
61 #include <bus/firewire/iec13213.h>
63 #include <dev/dcons/dcons.h>
64 #include <dev/firewire/firewire.h>
65 #include <dev/firewire/iec13213.h>
71 #include <sys/errno.h>
73 #define DCONS_POLL_HZ 100
74 #define DCONS_POLL_OFFLINE 2 /* sec */
78 #ifdef CSRVAL_VENDOR_PRIVATE
86 int poll_hz
= DCONS_POLL_HZ
;
88 #define IS_CONSOLE(p) ((p)->port == 0)
89 #define IS_GDB(p) ((p)->port == 1)
91 static struct dcons_state
{
96 #define F_READY (1 << 1)
97 #define F_RD_ONLY (1 << 2)
98 #define F_ALT_BREAK (1 << 3)
99 #define F_TELNET (1 << 4)
100 #define F_USE_CROM (1 << 5)
101 #define F_ONE_SHOT (1 << 6)
102 #define F_REPLAY (1 << 7)
118 struct addrinfo
*res
;
122 struct timespec zero
;
123 struct termios tsave
;
127 dread(struct dcons_state
*dc
, void *buf
, size_t n
, off_t offset
)
131 return (pread(dc
->fd
, buf
, n
, offset
));
133 return (kvm_read(dc
->kd
, offset
, buf
, n
));
139 dwrite(struct dcons_state
*dc
, void *buf
, size_t n
, off_t offset
)
141 if ((dc
->flags
& F_RD_ONLY
) != 0)
146 return (pwrite(dc
->fd
, buf
, n
, offset
));
148 return (kvm_write(dc
->kd
, offset
, buf
, n
));
154 dconschat_cleanup(int sig
)
156 struct dcons_state
*dc
;
160 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &dc
->tsave
);
163 printf("\n[dconschat exiting with signal %d ...]\n", sig
);
165 printf("\n[dconschat exiting...]\n");
171 dconschat_get_crom(struct dcons_state
*dc
)
175 u_int32_t buf
, hi
= 0, lo
= 0;
178 reg
= (struct csrreg
*)&buf
;
180 addr
= (addr
<< 32) | 0xf0000400;
181 for (i
= 20; i
< 0x400; i
+= 4) {
182 if (dread(dc
, &buf
, 4, addr
+ i
) < 0) {
184 warn("crom read faild");
189 printf("%d %02x %06x\n", state
, reg
->key
, reg
->val
);
192 if (reg
->key
== CSRKEY_SPEC
&&
193 reg
->val
== CSRVAL_VENDOR_PRIVATE
)
197 if (reg
->key
== CSRKEY_VER
&&
198 reg
->val
== DCONS_CSR_VAL_VER
)
202 if (reg
->key
== DCONS_CSR_KEY_HI
)
204 else if (reg
->key
== DCONS_CSR_KEY_LO
) {
215 printf("addr: %06x %06x\n", hi
, lo
);
216 dc
->paddr
= ((off_t
)hi
<< 24) | lo
;
222 dconschat_ready(struct dcons_state
*dc
, int ready
, const char *reason
)
224 static char oldreason
[64] = "";
227 old
= (dc
->flags
& F_READY
) ? 1 : 0;
230 dc
->flags
|= F_READY
;
232 printf("[dcons connected]\r\n");
235 dc
->flags
&= ~F_READY
;
236 if (strncmp(oldreason
, reason
, sizeof(oldreason
)) != 0) {
237 printf("[dcons disconnected (%s)]\r\n", reason
);
238 strlcpy(oldreason
, reason
, sizeof(oldreason
));
244 dconschat_fetch_header(struct dcons_state
*dc
)
247 struct dcons_buf dbuf
;
251 if (dc
->paddr
== 0 && (dc
->flags
& F_USE_CROM
) != 0) {
252 if (dconschat_get_crom(dc
)) {
253 dconschat_ready(dc
, 0, "get crom failed");
259 if (dread(dc
, &dbuf
, DCONS_HEADER_SIZE
, dc
->paddr
) < 0) {
260 dconschat_ready(dc
, 0, "read header failed");
263 if (dbuf
.magic
!= htonl(DCONS_MAGIC
)) {
264 if ((dc
->flags
& F_USE_CROM
) !=0)
266 snprintf(ebuf
, sizeof(ebuf
), "wrong magic 0x%08x", dbuf
.magic
);
267 dconschat_ready(dc
, 0, ebuf
);
270 if (ntohl(dbuf
.version
) != DCONS_VERSION
) {
271 snprintf(ebuf
, sizeof(ebuf
),
272 "wrong version %d,%d",
273 ntohl(dbuf
.version
), DCONS_VERSION
);
275 dconschat_ready(dc
, 0, ebuf
);
279 for (j
= 0; j
< DCONS_NPORT
; j
++) {
280 struct dcons_ch
*o
, *i
;
285 newbuf
= dc
->paddr
+ ntohl(dbuf
.ooffset
[j
]);
286 o
->size
= ntohl(dbuf
.osize
[j
]);
288 if (newbuf
!= o
->buf
) {
289 /* buffer address has changes */
291 o
->gen
= ntohl(dbuf
.optr
[j
]) >> DCONS_GEN_SHIFT
;
292 o
->pos
= ntohl(dbuf
.optr
[j
]) & DCONS_POS_MASK
;
297 i
->size
= ntohl(dbuf
.isize
[j
]);
298 i
->gen
= ntohl(dbuf
.iptr
[j
]) >> DCONS_GEN_SHIFT
;
299 i
->pos
= ntohl(dbuf
.iptr
[j
]) & DCONS_POS_MASK
;
300 i
->buf
= dc
->paddr
+ ntohl(dbuf
.ioffset
[j
]);
303 printf("port %d size offset gen pos\n", j
);
304 printf("output: %5d %6d %5d %5d\n"
305 "input : %5d %6d %5d %5d\n",
306 o
->size
, ntohl(dbuf
.ooffset
[j
]), o
->gen
, o
->pos
,
307 i
->size
, ntohl(dbuf
.ioffset
[j
]), i
->gen
, i
->pos
);
310 if (IS_CONSOLE(&dc
->port
[j
]) && new &&
311 (dc
->flags
& F_REPLAY
) !=0) {
318 dconschat_ready(dc
, 1, NULL
);
323 dconschat_get_ptr (struct dcons_state
*dc
) {
325 u_int32_t ptr
[DCONS_NPORT
*2+1];
326 static int retry
= RETRY
;
329 dlen
= dread(dc
, &ptr
, sizeof(ptr
),
330 dc
->paddr
+ __offsetof(struct dcons_buf
, magic
));
333 if (errno
== ETIMEDOUT
)
336 dconschat_ready(dc
, 0, "get ptr failed");
339 if (ptr
[0] != htonl(DCONS_MAGIC
)) {
340 dconschat_ready(dc
, 0, "wrong magic");
344 for (i
= 0; i
< DCONS_NPORT
; i
++) {
345 dc
->port
[i
].optr
= ntohl(ptr
[i
+ 1]);
346 dc
->port
[i
].iptr
= ntohl(ptr
[DCONS_NPORT
+ i
+ 1]);
351 #define MAX_XFER 2048
353 dconschat_read_dcons(struct dcons_state
*dc
, int port
, char *buf
, int len
)
356 u_int32_t ptr
, pos
, gen
, next_gen
;
357 int rlen
, dlen
, lost
;
360 ch
= &dc
->port
[port
].o
;
361 ptr
= dc
->port
[port
].optr
;
362 gen
= ptr
>> DCONS_GEN_SHIFT
;
363 pos
= ptr
& DCONS_POS_MASK
;
364 if (gen
== ch
->gen
&& pos
== ch
->pos
)
367 next_gen
= DCONS_NEXT_GEN(ch
->gen
);
368 /* XXX sanity check */
369 if (gen
== ch
->gen
) {
372 lost
= ch
->size
* DCONS_GEN_MASK
- ch
->pos
;
374 } else if (gen
== next_gen
) {
377 lost
= pos
- ch
->pos
;
380 lost
= gen
- ch
->gen
;
382 lost
+= DCONS_GEN_MASK
;
384 printf("[genskip %d]", lost
);
385 lost
= lost
* ch
->size
- ch
->pos
;
389 /* generation skipped !! */
392 printf("[lost %d]", lost
);
395 rlen
= pos
- ch
->pos
;
397 rlen
= ch
->size
- ch
->pos
;
406 printf("[%d]", rlen
);
412 dlen
= dread(dc
, buf
, rlen
, ch
->buf
+ ch
->pos
);
414 if (errno
== ETIMEDOUT
)
417 dconschat_ready(dc
, 0, "read buffer failed");
421 warnx("dlen(%d) != rlen(%d)\n", dlen
, rlen
);
423 if (ch
->pos
>= ch
->size
) {
427 printf("read_dcons: gen=%d", ch
->gen
);
433 dconschat_write_dcons(struct dcons_state
*dc
, int port
, char *buf
, int blen
)
440 ch
= &dc
->port
[port
].i
;
441 ptr
= dc
->port
[port
].iptr
;
443 /* the others may advance the pointer sync with it */
444 ch
->gen
= ptr
>> DCONS_GEN_SHIFT
;
445 ch
->pos
= ptr
& DCONS_POS_MASK
;
448 wlen
= MIN(blen
, (int)(ch
->size
- ch
->pos
));
449 wlen
= MIN(wlen
, MAX_XFER
);
450 len
= dwrite(dc
, buf
, wlen
, ch
->buf
+ ch
->pos
);
452 if (errno
== ETIMEDOUT
)
454 continue; /* try again */
455 dconschat_ready(dc
, 0, "write buffer failed");
461 if (ch
->pos
>= ch
->size
) {
462 ch
->gen
= DCONS_NEXT_GEN(ch
->gen
);
465 printf("write_dcons: gen=%d", ch
->gen
);
470 ptr
= DCONS_MAKE_PTR(ch
);
471 dc
->port
[port
].iptr
= ptr
;
474 printf("(iptr: 0x%x)", ptr
);
476 len
= dwrite(dc
, &ptr
, sizeof(u_int32_t
),
477 dc
->paddr
+ __offsetof(struct dcons_buf
, iptr
[port
]));
479 if (errno
== ETIMEDOUT
)
482 dconschat_ready(dc
, 0, "write ptr failed");
489 dconschat_write_socket(int fd
, char *buf
, int len
)
500 dconschat_init_socket(struct dcons_state
*dc
, int port
, char *host
, int sport
)
502 struct addrinfo hints
, *res
;
506 struct dcons_port
*p
;
510 p
->infd
= p
->outfd
= -1;
517 /* Use stdin and stdout */
518 p
->infd
= STDIN_FILENO
;
519 p
->outfd
= STDOUT_FILENO
;
522 tcgetattr(STDIN_FILENO
, &dc
->tsave
) == 0) {
527 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &traw
);
530 EV_SET(&kev
, p
->infd
, EVFILT_READ
, EV_ADD
, NOTE_LOWAT
, 1,
532 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
536 memset(&hints
, 0, sizeof(hints
));
537 hints
.ai_flags
= AI_PASSIVE
;
538 #if 1 /* gdb can talk v4 only */
539 hints
.ai_family
= PF_INET
;
541 hints
.ai_family
= PF_UNSPEC
;
543 hints
.ai_socktype
= SOCK_STREAM
;
544 hints
.ai_protocol
= 0;
547 printf("%s:%d for port %d\n",
548 host
== NULL
? "*" : host
, sport
, port
);
549 snprintf(service
, sizeof(service
), "%d", sport
);
550 error
= getaddrinfo(host
, service
, &hints
, &res
);
552 errx(1, "tcp/%s: %s\n", service
, gai_strerror(error
));
554 p
->s
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
557 setsockopt(p
->s
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
));
559 if (bind(p
->s
, p
->res
->ai_addr
, p
->res
->ai_addrlen
) < 0) {
562 if (listen(p
->s
, 1) < 0)
564 EV_SET(&kev
, p
->s
, EVFILT_READ
, EV_ADD
| EV_ONESHOT
, 0, 0, (void *)p
);
565 error
= kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->to
);
572 dconschat_accept_socket(struct dcons_state
*dc
, struct dcons_port
*p
)
577 /* accept connection */
578 foo
= p
->res
->ai_addrlen
;
579 ns
= accept(p
->s
, p
->res
->ai_addr
, &foo
);
583 printf("port%d accepted\n", p
->port
);
585 flags
= fcntl(ns
, F_GETFL
, 0);
587 fcntl(ns
, F_SETFL
, flags
);
589 if (IS_CONSOLE(p
) && (dc
->flags
& F_TELNET
) != 0) {
590 char sga
[] = {IAC
, WILL
, TELOPT_SGA
};
591 char linemode
[] = {IAC
, DONT
, TELOPT_LINEMODE
};
592 char echo
[] = {IAC
, WILL
, TELOPT_ECHO
};
593 char bin
[] = {IAC
, DO
, TELOPT_BINARY
};
595 write(ns
, sga
, sizeof(sga
));
596 write(ns
, linemode
, sizeof(linemode
));
597 write(ns
, echo
, sizeof(echo
));
598 write(ns
, bin
, sizeof(bin
));
603 p
->infd
= p
->outfd
= ns
;
604 EV_SET(&kev
, ns
, EVFILT_READ
, EV_ADD
, NOTE_LOWAT
, 1, (void *)p
);
605 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
610 dconschat_read_filter(struct dcons_state
*dc
, struct dcons_port
*p
,
611 u_char
*sp
, int slen
, u_char
*dp
, int *dlen
)
613 static u_char abreak
[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
617 if ((dc
->flags
& F_TELNET
) != 0) {
618 /* XXX Telent workarounds */
619 if (p
->skip_read
-- > 0) {
634 printf("(0 stripped)");
640 switch (dc
->escape_state
) {
642 if (*sp
== KEY_TILDE
)
643 dc
->escape_state
= STATE2
;
645 dc
->escape_state
= STATE0
;
648 dc
->escape_state
= STATE0
;
650 dconschat_cleanup(0);
653 dc
->escape_state
= STATE1
;
654 } else if (IS_GDB(p
)) {
655 /* GDB: ^C -> CR+~+^B */
656 if (*sp
== 0x3 && (dc
->flags
& F_ALT_BREAK
) != 0) {
657 bcopy(abreak
, dp
, 3);
661 /* discard rest of the packet */
675 dconschat_read_socket(struct dcons_state
*dc
, struct dcons_port
*p
)
679 char rbuf
[MAX_XFER
], wbuf
[MAX_XFER
+2];
681 if ((len
= read(p
->infd
, rbuf
, sizeof(rbuf
))) > 0) {
683 dconschat_read_filter(dc
, p
, rbuf
, len
, wbuf
, &wlen
);
684 /* XXX discard if not ready*/
685 if (wlen
> 0 && (dc
->flags
& F_READY
) != 0) {
686 dconschat_write_dcons(dc
, p
->port
, wbuf
, wlen
);
689 printf("(%s)\n", wbuf
);
692 printf("(%d)", wlen
);
699 warnx("port%d: closed", p
->port
);
701 warn("port%d: read", p
->port
);
703 EV_SET(&kev
, p
->infd
, EVFILT_READ
,
704 EV_DELETE
, 0, 0, NULL
);
705 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
708 /* XXX exit for pipe case XXX */
709 EV_SET(&kev
, p
->s
, EVFILT_READ
,
710 EV_ADD
| EV_ONESHOT
, 0, 0, (void *) p
);
711 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
712 p
->infd
= p
->outfd
= -1;
718 dconschat_proc_socket(struct dcons_state
*dc
)
720 struct kevent elist
[NEVENT
], *e
;
722 struct dcons_port
*p
;
724 n
= kevent(dc
->kq
, NULL
, 0, elist
, NEVENT
, &dc
->to
);
725 for (i
= 0; i
< n
; i
++) {
727 p
= (struct dcons_port
*)e
->udata
;
728 if (e
->ident
== (uintptr_t)p
->s
) {
729 dconschat_accept_socket(dc
, p
);
731 dconschat_read_socket(dc
, p
);
738 dconschat_proc_dcons(struct dcons_state
*dc
)
740 int port
, len
, error
;
742 struct dcons_port
*p
;
744 error
= dconschat_get_ptr(dc
);
746 /* XXX we should stop write operation too. */
749 for (port
= 0; port
< DCONS_NPORT
; port
++) {
753 while ((len
= dconschat_read_dcons(dc
, port
, buf
,
755 dconschat_write_socket(p
->outfd
, buf
, len
);
756 dconschat_get_ptr(dc
);
758 if ((dc
->flags
& F_ONE_SHOT
) != 0 && len
<= 0)
759 dconschat_cleanup(0);
765 dconschat_start_session(struct dcons_state
*dc
)
770 if ((dc
->flags
& F_READY
) == 0 &&
771 (++counter
% (poll_hz
* DCONS_POLL_OFFLINE
)) == 0)
772 dconschat_fetch_header(dc
);
773 if ((dc
->flags
& F_READY
) != 0)
774 dconschat_proc_dcons(dc
);
775 dconschat_proc_socket(dc
);
784 "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
785 "\t\t\t[-M core] [-N system]\n"
786 "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
787 "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
789 "\t-w listen on wildcard address rather than localhost\n"
790 "\t-r replay old buffer on connection\n"
792 "\t-T enable Telnet protocol workaround on console port\n"
793 "\t-1 one shot: read buffer and exit\n"
794 "\t-h polling rate\n"
795 "\t-C port number for console port\n"
796 "\t-G port number for gdb port\n"
801 "\t-u specify unit number of the bus\n"
802 "\t-t EUI64 of target host (must be specified)\n"
803 "\t-a physical address of dcons buffer on target host\n"
808 main(int argc
, char **argv
)
810 struct dcons_state
*dc
;
813 char devicename
[256], *core
= NULL
, *sysfile
= NULL
;
815 int unit
=0, wildcard
=0;
816 int port
[DCONS_NPORT
];
818 bzero(&sc
, sizeof(sc
));
820 dc
->flags
|= USE_CROM
? F_USE_CROM
: 0;
823 port
[0] = 0; /* stdin/out for console */
824 port
[1] = -1; /* disable gdb port */
826 while ((ch
= getopt(argc
, argv
, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
829 dc
->paddr
= strtoull(optarg
, NULL
, 0);
830 dc
->flags
&= ~F_USE_CROM
;
833 dc
->flags
|= F_ALT_BREAK
;
836 poll_hz
= strtoul(optarg
, NULL
, 0);
838 poll_hz
= DCONS_POLL_HZ
;
841 dc
->flags
|= F_REPLAY
;
844 if (eui64_hostton(optarg
, &target
) != 0 &&
845 eui64_aton(optarg
, &target
) != 0)
846 errx(1, "invalid target: %s", optarg
);
847 eui
.hi
= ntohl(*(u_int32_t
*)&(target
.octet
[0]));
848 eui
.lo
= ntohl(*(u_int32_t
*)&(target
.octet
[4]));
852 unit
= strtol(optarg
, NULL
, 0);
861 port
[0] = strtol(optarg
, NULL
, 0);
864 port
[1] = strtol(optarg
, NULL
, 0);
873 dc
->flags
|= F_RD_ONLY
;
876 dc
->flags
|= F_TELNET
;
879 dc
->flags
|= F_ONE_SHOT
| F_REPLAY
;
885 if (dc
->paddr
== 0 && (dc
->flags
& F_USE_CROM
) == 0) {
886 warnx("no address specified");
890 if (port
[0] < 0 && port
[1] < 0) {
891 warnx("no port specified");
895 /* set signal handler */
896 signal(SIGHUP
, dconschat_cleanup
);
897 signal(SIGINT
, dconschat_cleanup
);
898 signal(SIGPIPE
, dconschat_cleanup
);
899 signal(SIGTERM
, dconschat_cleanup
);
905 for (i
= 0; i
< MAXDEV
; i
++) {
906 snprintf(devicename
, sizeof(devicename
),
907 "/dev/fwmem%d.%d", unit
, i
);
908 dc
->fd
= open(devicename
, O_RDWR
);
914 error
= ioctl(dc
->fd
, FW_SDEUI64
, &eui
);
920 struct nlist nl
[] = {{.n_name
= "dcons_buf"}, {.n_name
= ""}};
923 dc
->kd
= kvm_open(sysfile
, core
, NULL
,
924 (dc
->flags
& F_RD_ONLY
) ? O_RDONLY
: O_RDWR
, "dconschat");
928 if (kvm_nlist(dc
->kd
, nl
) < 0)
929 errx(1, "kvm_nlist: %s", kvm_geterr(dc
->kd
));
931 if (kvm_read(dc
->kd
, nl
[0].n_value
, &dcons_buf
,
933 errx(1, "kvm_read: %s", kvm_geterr(dc
->kd
));
934 dc
->paddr
= (uintptr_t)dcons_buf
;
936 printf("dcons_buf: 0x%x\n", (uint
)dc
->paddr
);
940 dconschat_fetch_header(dc
);
949 dc
->to
.tv_nsec
= 1000 * 1000 * 1000 / poll_hz
;
952 dc
->zero
.tv_nsec
= 0;
953 for (i
= 0; i
< DCONS_NPORT
; i
++)
954 dconschat_init_socket(dc
, i
,
955 wildcard
? NULL
: __DECONST(char *, "localhost"), port
[i
]);
957 dconschat_start_session(dc
);
959 for (i
= 0; i
< DCONS_NPORT
; i
++) {
960 freeaddrinfo(dc
->port
[i
].res
);