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
); fflush(stdout
);
410 dlen
= dread(dc
, buf
, rlen
, ch
->buf
+ ch
->pos
);
412 if (errno
== ETIMEDOUT
)
415 dconschat_ready(dc
, 0, "read buffer failed");
419 warnx("dlen(%d) != rlen(%d)\n", dlen
, rlen
);
421 if (ch
->pos
>= ch
->size
) {
425 printf("read_dcons: gen=%d", ch
->gen
);
431 dconschat_write_dcons(struct dcons_state
*dc
, int port
, char *buf
, int blen
)
438 ch
= &dc
->port
[port
].i
;
439 ptr
= dc
->port
[port
].iptr
;
441 /* the others may advance the pointer sync with it */
442 ch
->gen
= ptr
>> DCONS_GEN_SHIFT
;
443 ch
->pos
= ptr
& DCONS_POS_MASK
;
446 wlen
= MIN(blen
, (int)(ch
->size
- ch
->pos
));
447 wlen
= MIN(wlen
, MAX_XFER
);
448 len
= dwrite(dc
, buf
, wlen
, ch
->buf
+ ch
->pos
);
450 if (errno
== ETIMEDOUT
)
452 continue; /* try again */
453 dconschat_ready(dc
, 0, "write buffer failed");
459 if (ch
->pos
>= ch
->size
) {
460 ch
->gen
= DCONS_NEXT_GEN(ch
->gen
);
463 printf("write_dcons: gen=%d", ch
->gen
);
468 ptr
= DCONS_MAKE_PTR(ch
);
469 dc
->port
[port
].iptr
= ptr
;
472 printf("(iptr: 0x%x)", ptr
);
474 len
= dwrite(dc
, &ptr
, sizeof(u_int32_t
),
475 dc
->paddr
+ __offsetof(struct dcons_buf
, iptr
[port
]));
477 if (errno
== ETIMEDOUT
)
480 dconschat_ready(dc
, 0, "write ptr failed");
487 dconschat_write_socket(int fd
, char *buf
, int len
)
498 dconschat_init_socket(struct dcons_state
*dc
, int port
, char *host
, int sport
)
500 struct addrinfo hints
, *res
;
504 struct dcons_port
*p
;
508 p
->infd
= p
->outfd
= -1;
515 /* Use stdin and stdout */
516 p
->infd
= STDIN_FILENO
;
517 p
->outfd
= STDOUT_FILENO
;
520 tcgetattr(STDIN_FILENO
, &dc
->tsave
) == 0) {
525 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &traw
);
528 EV_SET(&kev
, p
->infd
, EVFILT_READ
, EV_ADD
, NOTE_LOWAT
, 1,
530 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
534 memset(&hints
, 0, sizeof(hints
));
535 hints
.ai_flags
= AI_PASSIVE
;
536 #if 1 /* gdb can talk v4 only */
537 hints
.ai_family
= PF_INET
;
539 hints
.ai_family
= PF_UNSPEC
;
541 hints
.ai_socktype
= SOCK_STREAM
;
542 hints
.ai_protocol
= 0;
545 printf("%s:%d for port %d\n",
546 host
== NULL
? "*" : host
, sport
, port
);
547 snprintf(service
, sizeof(service
), "%d", sport
);
548 error
= getaddrinfo(host
, service
, &hints
, &res
);
550 errx(1, "tcp/%s: %s\n", service
, gai_strerror(error
));
552 p
->s
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
555 setsockopt(p
->s
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
));
557 if (bind(p
->s
, p
->res
->ai_addr
, p
->res
->ai_addrlen
) < 0) {
560 if (listen(p
->s
, 1) < 0)
562 EV_SET(&kev
, p
->s
, EVFILT_READ
, EV_ADD
| EV_ONESHOT
, 0, 0, (void *)p
);
563 error
= kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->to
);
570 dconschat_accept_socket(struct dcons_state
*dc
, struct dcons_port
*p
)
575 /* accept connection */
576 foo
= p
->res
->ai_addrlen
;
577 ns
= accept(p
->s
, p
->res
->ai_addr
, &foo
);
581 printf("port%d accepted\n", p
->port
);
583 flags
= fcntl(ns
, F_GETFL
, 0);
585 fcntl(ns
, F_SETFL
, flags
);
587 if (IS_CONSOLE(p
) && (dc
->flags
& F_TELNET
) != 0) {
588 char sga
[] = {IAC
, WILL
, TELOPT_SGA
};
589 char linemode
[] = {IAC
, DONT
, TELOPT_LINEMODE
};
590 char echo
[] = {IAC
, WILL
, TELOPT_ECHO
};
591 char bin
[] = {IAC
, DO
, TELOPT_BINARY
};
593 write(ns
, sga
, sizeof(sga
));
594 write(ns
, linemode
, sizeof(linemode
));
595 write(ns
, echo
, sizeof(echo
));
596 write(ns
, bin
, sizeof(bin
));
601 p
->infd
= p
->outfd
= ns
;
602 EV_SET(&kev
, ns
, EVFILT_READ
, EV_ADD
, NOTE_LOWAT
, 1, (void *)p
);
603 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
608 dconschat_read_filter(struct dcons_state
*dc
, struct dcons_port
*p
,
609 u_char
*sp
, int slen
, u_char
*dp
, int *dlen
)
611 static u_char abreak
[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
615 if ((dc
->flags
& F_TELNET
) != 0) {
616 /* XXX Telent workarounds */
617 if (p
->skip_read
-- > 0) {
632 printf("(0 stripped)");
638 switch (dc
->escape_state
) {
640 if (*sp
== KEY_TILDE
)
641 dc
->escape_state
= STATE2
;
643 dc
->escape_state
= STATE0
;
646 dc
->escape_state
= STATE0
;
648 dconschat_cleanup(0);
651 dc
->escape_state
= STATE1
;
652 } else if (IS_GDB(p
)) {
653 /* GDB: ^C -> CR+~+^B */
654 if (*sp
== 0x3 && (dc
->flags
& F_ALT_BREAK
) != 0) {
655 bcopy(abreak
, dp
, 3);
659 /* discard rest of the packet */
673 dconschat_read_socket(struct dcons_state
*dc
, struct dcons_port
*p
)
677 char rbuf
[MAX_XFER
], wbuf
[MAX_XFER
+2];
679 if ((len
= read(p
->infd
, rbuf
, sizeof(rbuf
))) > 0) {
681 dconschat_read_filter(dc
, p
, rbuf
, len
, wbuf
, &wlen
);
682 /* XXX discard if not ready*/
683 if (wlen
> 0 && (dc
->flags
& F_READY
) != 0) {
684 dconschat_write_dcons(dc
, p
->port
, wbuf
, wlen
);
687 printf("(%s)\n", wbuf
);
690 printf("(%d)", wlen
);
697 warnx("port%d: closed", p
->port
);
699 warn("port%d: read", p
->port
);
701 EV_SET(&kev
, p
->infd
, EVFILT_READ
,
702 EV_DELETE
, 0, 0, NULL
);
703 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
706 /* XXX exit for pipe case XXX */
707 EV_SET(&kev
, p
->s
, EVFILT_READ
,
708 EV_ADD
| EV_ONESHOT
, 0, 0, (void *) p
);
709 kevent(dc
->kq
, &kev
, 1, NULL
, 0, &dc
->zero
);
710 p
->infd
= p
->outfd
= -1;
716 dconschat_proc_socket(struct dcons_state
*dc
)
718 struct kevent elist
[NEVENT
], *e
;
720 struct dcons_port
*p
;
722 n
= kevent(dc
->kq
, NULL
, 0, elist
, NEVENT
, &dc
->to
);
723 for (i
= 0; i
< n
; i
++) {
725 p
= (struct dcons_port
*)e
->udata
;
726 if (e
->ident
== (uintptr_t)p
->s
) {
727 dconschat_accept_socket(dc
, p
);
729 dconschat_read_socket(dc
, p
);
736 dconschat_proc_dcons(struct dcons_state
*dc
)
738 int port
, len
, error
;
740 struct dcons_port
*p
;
742 error
= dconschat_get_ptr(dc
);
744 /* XXX we should stop write operation too. */
747 for (port
= 0; port
< DCONS_NPORT
; port
++) {
751 while ((len
= dconschat_read_dcons(dc
, port
, buf
,
753 dconschat_write_socket(p
->outfd
, buf
, len
);
754 dconschat_get_ptr(dc
);
756 if ((dc
->flags
& F_ONE_SHOT
) != 0 && len
<= 0)
757 dconschat_cleanup(0);
763 dconschat_start_session(struct dcons_state
*dc
)
768 if ((dc
->flags
& F_READY
) == 0 &&
769 (++counter
% (poll_hz
* DCONS_POLL_OFFLINE
)) == 0)
770 dconschat_fetch_header(dc
);
771 if ((dc
->flags
& F_READY
) != 0)
772 dconschat_proc_dcons(dc
);
773 dconschat_proc_socket(dc
);
782 "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
783 "\t\t\t[-M core] [-N system]\n"
784 "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
785 "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
787 "\t-w listen on wildcard address rather than localhost\n"
788 "\t-r replay old buffer on connection\n"
790 "\t-T enable Telnet protocol workaround on console port\n"
791 "\t-1 one shot: read buffer and exit\n"
792 "\t-h polling rate\n"
793 "\t-C port number for console port\n"
794 "\t-G port number for gdb port\n"
799 "\t-u specify unit number of the bus\n"
800 "\t-t EUI64 of target host (must be specified)\n"
801 "\t-a physical address of dcons buffer on target host\n"
806 main(int argc
, char **argv
)
808 struct dcons_state
*dc
;
811 char devicename
[256], *core
= NULL
, *sysfile
= NULL
;
813 int unit
=0, wildcard
=0;
814 int port
[DCONS_NPORT
];
816 bzero(&sc
, sizeof(sc
));
818 dc
->flags
|= USE_CROM
? F_USE_CROM
: 0;
821 port
[0] = 0; /* stdin/out for console */
822 port
[1] = -1; /* disable gdb port */
824 while ((ch
= getopt(argc
, argv
, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
827 dc
->paddr
= strtoull(optarg
, NULL
, 0);
828 dc
->flags
&= ~F_USE_CROM
;
831 dc
->flags
|= F_ALT_BREAK
;
834 poll_hz
= strtoul(optarg
, NULL
, 0);
836 poll_hz
= DCONS_POLL_HZ
;
839 dc
->flags
|= F_REPLAY
;
842 if (eui64_hostton(optarg
, &target
) != 0 &&
843 eui64_aton(optarg
, &target
) != 0)
844 errx(1, "invalid target: %s", optarg
);
845 eui
.hi
= ntohl(*(u_int32_t
*)&(target
.octet
[0]));
846 eui
.lo
= ntohl(*(u_int32_t
*)&(target
.octet
[4]));
850 unit
= strtol(optarg
, NULL
, 0);
859 port
[0] = strtol(optarg
, NULL
, 0);
862 port
[1] = strtol(optarg
, NULL
, 0);
871 dc
->flags
|= F_RD_ONLY
;
874 dc
->flags
|= F_TELNET
;
877 dc
->flags
|= F_ONE_SHOT
| F_REPLAY
;
883 if (dc
->paddr
== 0 && (dc
->flags
& F_USE_CROM
) == 0) {
884 warnx("no address specified");
888 if (port
[0] < 0 && port
[1] < 0) {
889 warnx("no port specified");
893 /* set signal handler */
894 signal(SIGHUP
, dconschat_cleanup
);
895 signal(SIGINT
, dconschat_cleanup
);
896 signal(SIGPIPE
, dconschat_cleanup
);
897 signal(SIGTERM
, dconschat_cleanup
);
903 for (i
= 0; i
< MAXDEV
; i
++) {
904 snprintf(devicename
, sizeof(devicename
),
905 "/dev/fwmem%d.%d", unit
, i
);
906 dc
->fd
= open(devicename
, O_RDWR
);
912 error
= ioctl(dc
->fd
, FW_SDEUI64
, &eui
);
918 struct nlist nl
[] = {{.n_name
= "dcons_buf"}, {.n_name
= ""}};
921 dc
->kd
= kvm_open(sysfile
, core
, NULL
,
922 (dc
->flags
& F_RD_ONLY
) ? O_RDONLY
: O_RDWR
, "dconschat");
926 if (kvm_nlist(dc
->kd
, nl
) < 0)
927 errx(1, "kvm_nlist: %s", kvm_geterr(dc
->kd
));
929 if (kvm_read(dc
->kd
, nl
[0].n_value
, &dcons_buf
,
931 errx(1, "kvm_read: %s", kvm_geterr(dc
->kd
));
932 dc
->paddr
= (uintptr_t)dcons_buf
;
934 printf("dcons_buf: 0x%x\n", (uint
)dc
->paddr
);
938 dconschat_fetch_header(dc
);
947 dc
->to
.tv_nsec
= 1000 * 1000 * 1000 / poll_hz
;
950 dc
->zero
.tv_nsec
= 0;
951 for (i
= 0; i
< DCONS_NPORT
; i
++)
952 dconschat_init_socket(dc
, i
,
953 wildcard
? NULL
: __DECONST(char *, "localhost"), port
[i
]);
955 dconschat_start_session(dc
);
957 for (i
= 0; i
< DCONS_NPORT
; i
++) {
958 freeaddrinfo(dc
->port
[i
].res
);