2 * Copyright (c) 2016-2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
4 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
5 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
6 * Copyright (c) 2001 Markus Friedl. All rights reserved.
7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
8 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include "got_compat.h"
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/queue.h>
29 #include <sys/socket.h>
33 #include <netinet/in.h>
35 #include <arpa/inet.h>
51 #include "got_reference.h"
55 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
57 TAILQ_ENTRY
(file
) entry
;
63 struct file
*newfile
(const char *, int);
64 static void closefile
(struct file
*);
65 int check_file_secrecy
(int, const char *);
68 int yyerror(const char *, ...
)
69 __attribute__
((__format__
(printf
, 1, 2)))
70 __attribute__
((__nonnull__
(1)));
71 int kw_cmp
(const void *, const void *);
77 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
79 TAILQ_ENTRY
(sym
) entry
;
86 int symset
(const char *, const char *, int);
87 char *symget
(const char *);
91 static struct gotwebd
*gotwebd
;
92 static struct server
*new_srv
;
93 static struct server
*conf_new_server
(const char *);
94 int getservice
(const char *);
97 int get_addrs
(const char *, const char *, struct server
*);
98 int addr_dup_check
(struct addresslist
*, struct address
*,
99 const char *, const char *);
100 int add_addr
(struct server
*, struct address
*);
112 %token LISTEN WWW_PATH MAX_REPOS SITE_NAME SITE_OWNER SITE_LINK LOGO
113 %token LOGO_URL SHOW_REPO_OWNER SHOW_REPO_AGE SHOW_REPO_DESCRIPTION
114 %token MAX_REPOS_DISPLAY REPOS_PATH MAX_COMMITS_DISPLAY ON ERROR
115 %token SHOW_SITE_OWNER SHOW_REPO_CLONEURL PORT PREFORK RESPECT_EXPORTOK
116 %token UNIX_SOCKET UNIX_SOCKET_NAME SERVER CHROOT CUSTOM_CSS SOCKET
118 %token
<v.
string> STRING
119 %token
<v.number
> NUMBER
120 %type
<v.number
> boolean
121 %type
<v.
string> listen_addr
125 grammar
: /* empty */
127 | grammar varset
'\n'
129 | grammar server
'\n'
130 | grammar
error '\n' { file
->errors
++; }
133 varset
: STRING
'=' STRING
{
136 if
(isspace
((unsigned char)*s
)) {
137 yyerror("macro name cannot contain "
144 if
(symset
($1, $3, 0) == -1)
145 fatal
("cannot store variable");
152 if
(strcasecmp
($1, "1") == 0 ||
153 strcasecmp
($1, "on") == 0)
155 else if
(strcasecmp
($1, "0") == 0 ||
156 strcasecmp
($1, "off") == 0)
159 yyerror("invalid boolean value '%s'", $1);
167 if
($1 != 0 && $1 != 1) {
168 yyerror("invalid boolean value '%lld'", $1);
175 listen_addr
: '*' { $$
= NULL
; }
179 main
: PREFORK NUMBER
{
180 if
($2 <= 0 ||
$2 > PROC_MAX_INSTANCES
) {
181 yyerror("prefork is %s: %lld",
182 $2 <= 0 ?
"too small" : "too large", $2);
185 gotwebd
->prefork_gotwebd
= $2;
189 yyerror("chroot path can't be an empty"
195 n
= strlcpy
(gotwebd
->httpd_chroot
, $2,
196 sizeof
(gotwebd
->httpd_chroot
));
197 if
(n
>= sizeof
(gotwebd
->httpd_chroot
)) {
198 yyerror("%s: httpd_chroot truncated", __func__
);
204 | UNIX_SOCKET boolean
{
205 gotwebd
->unix_socket
= $2;
207 | UNIX_SOCKET_NAME STRING
{
208 n
= snprintf
(gotwebd
->unix_socket_name
,
209 sizeof
(gotwebd
->unix_socket_name
), "%s%s",
210 gotwebd
->httpd_chroot
, $2);
212 (size_t)n
>= sizeof
(gotwebd
->unix_socket_name
)) {
213 yyerror("%s: unix_socket_name truncated",
222 server
: SERVER STRING
{
225 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
226 if
(strcmp
(srv
->name
, $2) == 0) {
227 yyerror("server name exists '%s'", $2);
233 new_srv
= conf_new_server
($2);
234 log_debug
("adding server %s", $2);
240 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
241 if
(strcmp
(srv
->name
, $2) == 0) {
242 yyerror("server name exists '%s'", $2);
248 new_srv
= conf_new_server
($2);
249 log_debug
("adding server %s", $2);
251 } '{' optnl serveropts2
'}' {
255 serveropts1
: REPOS_PATH STRING
{
256 n
= strlcpy
(new_srv
->repos_path
, $2,
257 sizeof
(new_srv
->repos_path
));
258 if
(n
>= sizeof
(new_srv
->repos_path
)) {
259 yyerror("%s: repos_path truncated", __func__
);
266 n
= strlcpy
(new_srv
->site_name
, $2,
267 sizeof
(new_srv
->site_name
));
268 if
(n
>= sizeof
(new_srv
->site_name
)) {
269 yyerror("%s: site_name truncated", __func__
);
275 | SITE_OWNER STRING
{
276 n
= strlcpy
(new_srv
->site_owner
, $2,
277 sizeof
(new_srv
->site_owner
));
278 if
(n
>= sizeof
(new_srv
->site_owner
)) {
279 yyerror("%s: site_owner truncated", __func__
);
286 n
= strlcpy
(new_srv
->site_link
, $2,
287 sizeof
(new_srv
->site_link
));
288 if
(n
>= sizeof
(new_srv
->site_link
)) {
289 yyerror("%s: site_link truncated", __func__
);
296 n
= strlcpy
(new_srv
->logo
, $2, sizeof
(new_srv
->logo
));
297 if
(n
>= sizeof
(new_srv
->logo
)) {
298 yyerror("%s: logo truncated", __func__
);
305 n
= strlcpy
(new_srv
->logo_url
, $2,
306 sizeof
(new_srv
->logo_url
));
307 if
(n
>= sizeof
(new_srv
->logo_url
)) {
308 yyerror("%s: logo_url truncated", __func__
);
314 | CUSTOM_CSS STRING
{
315 n
= strlcpy
(new_srv
->custom_css
, $2,
316 sizeof
(new_srv
->custom_css
));
317 if
(n
>= sizeof
(new_srv
->custom_css
)) {
318 yyerror("%s: custom_css truncated", __func__
);
324 | LISTEN ON listen_addr PORT STRING
{
325 if
(get_addrs
($3, $5, new_srv
) == -1) {
326 yyerror("could not get addrs");
331 new_srv
->fcgi_socket
= 1;
333 | LISTEN ON listen_addr PORT NUMBER
{
337 n
= snprintf
(portno
, sizeof
(portno
), "%lld",
339 if
(n
< 0 ||
(size_t)n
>= sizeof
(portno
))
340 fatalx
("port number too long: %lld",
343 if
(get_addrs
($3, portno
, new_srv
) == -1) {
344 yyerror("could not get addrs");
348 new_srv
->fcgi_socket
= 1;
350 | LISTEN ON SOCKET STRING
{
351 if
(strcasecmp
($4, "off") == 0) {
352 new_srv
->unix_socket
= 0;
357 new_srv
->unix_socket
= 1;
359 n
= snprintf
(new_srv
->unix_socket_name
,
360 sizeof
(new_srv
->unix_socket_name
), "%s%s",
361 gotwebd
->httpd_chroot
, $4);
363 (size_t)n
>= sizeof
(new_srv
->unix_socket_name
)) {
364 yyerror("%s: unix_socket_name truncated",
373 yyerror("max_repos is too small: %lld", $2);
376 new_srv
->max_repos
= $2;
378 | SHOW_SITE_OWNER boolean
{
379 new_srv
->show_site_owner
= $2;
381 | SHOW_REPO_OWNER boolean
{
382 new_srv
->show_repo_owner
= $2;
384 | SHOW_REPO_AGE boolean
{
385 new_srv
->show_repo_age
= $2;
387 | SHOW_REPO_DESCRIPTION boolean
{
388 new_srv
->show_repo_description
= $2;
390 | SHOW_REPO_CLONEURL boolean
{
391 new_srv
->show_repo_cloneurl
= $2;
393 | RESPECT_EXPORTOK boolean
{
394 new_srv
->respect_exportok
= $2;
396 | MAX_REPOS_DISPLAY NUMBER
{
398 yyerror("max_repos_display is too small: %lld",
402 new_srv
->max_repos_display
= $2;
404 | MAX_COMMITS_DISPLAY NUMBER
{
406 yyerror("max_commits_display is too small:"
410 new_srv
->max_commits_display
= $2;
414 serveropts2
: serveropts2 serveropts1 nl
421 optnl
: '\n' optnl
/* zero or more newlines */
433 yyerror(const char *fmt
, ...
)
440 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
441 fatalx
("yyerror vasprintf");
443 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
449 kw_cmp
(const void *k
, const void *e
)
451 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
457 /* This has to be sorted always. */
458 static const struct keywords keywords
[] = {
459 { "chroot", CHROOT
},
460 { "custom_css", CUSTOM_CSS
},
461 { "listen", LISTEN
},
463 { "logo_url", LOGO_URL
},
464 { "max_commits_display", MAX_COMMITS_DISPLAY
},
465 { "max_repos", MAX_REPOS
},
466 { "max_repos_display", MAX_REPOS_DISPLAY
},
469 { "prefork", PREFORK
},
470 { "repos_path", REPOS_PATH
},
471 { "respect_exportok", RESPECT_EXPORTOK
},
472 { "server", SERVER
},
473 { "show_repo_age", SHOW_REPO_AGE
},
474 { "show_repo_cloneurl", SHOW_REPO_CLONEURL
},
475 { "show_repo_description", SHOW_REPO_DESCRIPTION
},
476 { "show_repo_owner", SHOW_REPO_OWNER
},
477 { "show_site_owner", SHOW_SITE_OWNER
},
478 { "site_link", SITE_LINK
},
479 { "site_name", SITE_NAME
},
480 { "site_owner", SITE_OWNER
},
481 { "socket", SOCKET
},
482 { "unix_socket", UNIX_SOCKET
},
483 { "unix_socket_name", UNIX_SOCKET_NAME
},
485 const struct keywords
*p
;
487 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
488 sizeof
(keywords
[0]), kw_cmp
);
496 #define MAXPUSHBACK 128
498 unsigned char *parsebuf
;
500 unsigned char pushback_buffer
[MAXPUSHBACK
];
501 int pushback_index
= 0;
509 /* Read character from the parsebuffer instead of input. */
510 if
(parseindex
>= 0) {
511 c
= parsebuf
[parseindex
++];
520 return
(pushback_buffer
[--pushback_index
]);
523 c
= getc
(file
->stream
);
525 yyerror("reached end of file while parsing "
530 c
= getc
(file
->stream
);
532 next
= getc
(file
->stream
);
537 yylval.lineno
= file
->lineno
;
539 c
= getc
(file
->stream
);
555 if
(pushback_index
< MAXPUSHBACK
-1)
556 return
(pushback_buffer
[pushback_index
++] = c
);
568 /* Skip to either EOF or the first real EOL. */
571 c
= pushback_buffer
[--pushback_index
];
587 unsigned char buf
[8096];
588 unsigned char *p
, *val
;
595 while
(c
== ' ' || c
== '\t')
596 c
= lgetc
(0); /* nothing */
598 yylval.lineno
= file
->lineno
;
601 while
(c
!= '\n' && c
!= EOF
)
602 c
= lgetc
(0); /* nothing */
604 if
(c
== '$' && parsebuf
== NULL
) {
610 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
611 yyerror("string too long");
614 if
(isalnum
(c
) || c
== '_') {
624 yyerror("macro '%s' not defined", buf
);
643 } else if
(c
== '\\') {
644 next
= lgetc
(quotec
);
647 if
(next
== quotec || c
== ' ' || c
== '\t')
649 else if
(next
== '\n') {
654 } else if
(c
== quotec
) {
657 } else if
(c
== '\0') {
658 yyerror("syntax error");
661 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
662 yyerror("string too long");
667 yylval.v.
string = strdup
(buf
);
668 if
(yylval.v.
string == NULL
)
669 err
(1, "yylex: strdup");
673 #define allowed_to_end_number(x) \
674 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
676 if
(c
== '-' || isdigit
(c
)) {
679 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
680 yyerror("string too long");
684 } while
(c
!= EOF
&& isdigit
(c
));
686 if
(p
== buf
+ 1 && buf
[0] == '-')
688 if
(c
== EOF || allowed_to_end_number
(c
)) {
689 const char *errstr
= NULL
;
692 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
695 yyerror("\"%s\" invalid number: %s",
710 #define allowed_in_string(x) \
711 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
712 x
!= '{' && x
!= '}' && \
713 x
!= '!' && x
!= '=' && x
!= '#' && \
716 if
(isalnum
(c
) || c
== ':' || c
== '_') {
719 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
720 yyerror("string too long");
724 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
728 if
(token
== STRING
) {
729 yylval.v.
string = strdup
(buf
);
730 if
(yylval.v.
string == NULL
)
731 err
(1, "yylex: strdup");
736 yylval.lineno
= file
->lineno
;
745 check_file_secrecy
(int fd
, const char *fname
)
749 if
(fstat
(fd
, &st
)) {
750 log_warn
("cannot stat %s", fname
);
753 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
754 log_warnx
("%s: owner not root or current user", fname
);
757 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
758 log_warnx
("%s: group writable or world read/writable", fname
);
765 newfile
(const char *name
, int secret
)
769 nfile
= calloc
(1, sizeof
(struct file
));
774 nfile
->name
= strdup
(name
);
775 if
(nfile
->name
== NULL
) {
780 nfile
->stream
= fopen
(nfile
->name
, "r");
781 if
(nfile
->stream
== NULL
) {
782 /* no warning, we don't require a conf file */
787 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
788 fclose
(nfile
->stream
);
798 closefile
(struct file
*xfile
)
800 fclose
(xfile
->stream
);
806 add_default_server
(void)
808 new_srv
= conf_new_server
(D_SITENAME
);
809 log_debug
("%s: adding default server %s", __func__
, D_SITENAME
);
813 parse_config
(const char *filename
, struct gotwebd
*env
)
815 struct sym
*sym
, *next
;
817 if
(config_init
(env
) == -1)
818 fatalx
("failed to initialize configuration");
822 file
= newfile
(filename
, 0);
824 add_default_server
();
825 sockets_parse_sockets
(env
);
826 /* just return, as we don't require a conf file */
831 errors
= file
->errors
;
834 /* Free macros and check which have not been used. */
835 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
836 if
((gotwebd
->gotwebd_verbose
> 1) && !sym
->used
)
837 fprintf
(stderr
, "warning: macro '%s' not used\n",
842 TAILQ_REMOVE
(&symhead
, sym
, entry
);
850 /* just add default server if no config specified */
851 if
(gotwebd
->server_cnt
== 0)
852 add_default_server
();
854 /* setup our listening sockets */
855 sockets_parse_sockets
(env
);
861 conf_new_server
(const char *name
)
863 struct server
*srv
= NULL
;
865 srv
= calloc
(1, sizeof
(*srv
));
867 fatalx
("%s: calloc", __func__
);
869 n
= strlcpy
(srv
->name
, name
, sizeof
(srv
->name
));
870 if
(n
>= sizeof
(srv
->name
))
871 fatalx
("%s: strlcpy", __func__
);
872 n
= snprintf
(srv
->unix_socket_name
,
873 sizeof
(srv
->unix_socket_name
), "%s%s", D_HTTPD_CHROOT
,
875 if
(n
< 0 ||
(size_t)n
>= sizeof
(srv
->unix_socket_name
))
876 fatalx
("%s: snprintf", __func__
);
877 n
= strlcpy
(srv
->repos_path
, D_GOTPATH
,
878 sizeof
(srv
->repos_path
));
879 if
(n
>= sizeof
(srv
->repos_path
))
880 fatalx
("%s: strlcpy", __func__
);
881 n
= strlcpy
(srv
->site_name
, D_SITENAME
,
882 sizeof
(srv
->site_name
));
883 if
(n
>= sizeof
(srv
->site_name
))
884 fatalx
("%s: strlcpy", __func__
);
885 n
= strlcpy
(srv
->site_owner
, D_SITEOWNER
,
886 sizeof
(srv
->site_owner
));
887 if
(n
>= sizeof
(srv
->site_owner
))
888 fatalx
("%s: strlcpy", __func__
);
889 n
= strlcpy
(srv
->site_link
, D_SITELINK
,
890 sizeof
(srv
->site_link
));
891 if
(n
>= sizeof
(srv
->site_link
))
892 fatalx
("%s: strlcpy", __func__
);
893 n
= strlcpy
(srv
->logo
, D_GOTLOGO
,
895 if
(n
>= sizeof
(srv
->logo
))
896 fatalx
("%s: strlcpy", __func__
);
897 n
= strlcpy
(srv
->logo_url
, D_GOTURL
, sizeof
(srv
->logo_url
));
898 if
(n
>= sizeof
(srv
->logo_url
))
899 fatalx
("%s: strlcpy", __func__
);
900 n
= strlcpy
(srv
->custom_css
, D_GOTWEBCSS
, sizeof
(srv
->custom_css
));
901 if
(n
>= sizeof
(srv
->custom_css
))
902 fatalx
("%s: strlcpy", __func__
);
904 srv
->show_site_owner
= D_SHOWSOWNER
;
905 srv
->show_repo_owner
= D_SHOWROWNER
;
906 srv
->show_repo_age
= D_SHOWAGE
;
907 srv
->show_repo_description
= D_SHOWDESC
;
908 srv
->show_repo_cloneurl
= D_SHOWURL
;
909 srv
->respect_exportok
= D_RESPECTEXPORTOK
;
911 srv
->max_repos_display
= D_MAXREPODISP
;
912 srv
->max_commits_display
= D_MAXCOMMITDISP
;
913 srv
->max_repos
= D_MAXREPO
;
915 srv
->unix_socket
= 1;
916 srv
->fcgi_socket
= 0;
918 TAILQ_INIT
(&srv
->al
);
919 TAILQ_INSERT_TAIL
(&gotwebd
->servers
, srv
, entry
);
920 gotwebd
->server_cnt
++;
926 symset
(const char *nam
, const char *val
, int persist
)
930 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
931 if
(strcmp
(nam
, sym
->nam
) == 0)
936 if
(sym
->persist
== 1)
941 TAILQ_REMOVE
(&symhead
, sym
, entry
);
945 sym
= calloc
(1, sizeof
(*sym
));
949 sym
->nam
= strdup
(nam
);
950 if
(sym
->nam
== NULL
) {
954 sym
->val
= strdup
(val
);
955 if
(sym
->val
== NULL
) {
961 sym
->persist
= persist
;
962 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
967 cmdline_symset
(char *s
)
972 val
= strrchr
(s
, '=');
976 sym
= strndup
(s
, val
- s
);
978 fatal
("%s: strndup", __func__
);
980 ret
= symset
(sym
, val
+ 1, 1);
987 symget
(const char *nam
)
991 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
992 if
(strcmp
(nam
, sym
->nam
) == 0) {
1001 get_addrs
(const char *hostname
, const char *servname
, struct server
*new_srv
)
1003 struct addrinfo hints
, *res0
, *res
;
1005 struct sockaddr_in
*sin
;
1006 struct sockaddr_in6
*sin6
;
1009 memset
(&hints
, 0, sizeof
(hints
));
1010 hints.ai_family
= AF_UNSPEC
;
1011 hints.ai_socktype
= SOCK_STREAM
;
1012 hints.ai_flags
= AI_PASSIVE | AI_ADDRCONFIG
;
1013 error = getaddrinfo
(hostname
, servname
, &hints
, &res0
);
1015 log_warnx
("%s: could not parse \"%s:%s\": %s", __func__
,
1016 hostname
, servname
, gai_strerror
(error));
1020 for
(res
= res0
; res
; res
= res
->ai_next
) {
1021 if
((h
= calloc
(1, sizeof
(*h
))) == NULL
)
1024 if
(hostname
== NULL
) {
1025 strlcpy
(h
->ifname
, "*", sizeof
(h
->ifname
));
1027 if
(strlcpy
(h
->ifname
, hostname
, sizeof
(h
->ifname
)) >=
1028 sizeof
(h
->ifname
)) {
1029 log_warnx
("%s: address truncated: %s",
1030 __func__
, hostname
);
1037 h
->ai_family
= res
->ai_family
;
1038 h
->ai_socktype
= res
->ai_socktype
;
1039 h
->ai_protocol
= res
->ai_protocol
;
1040 memcpy
(&h
->ss
, res
->ai_addr
, res
->ai_addrlen
);
1041 h
->slen
= res
->ai_addrlen
;
1043 switch
(res
->ai_family
) {
1045 sin
= (struct sockaddr_in
*)res
->ai_addr
;
1046 h
->port
= ntohs
(sin
->sin_port
);
1049 sin6
= (struct sockaddr_in6
*)res
->ai_addr
;
1050 h
->port
= ntohs
(sin6
->sin6_port
);
1053 fatalx
("unknown address family %d", res
->ai_family
);
1056 if
(add_addr
(new_srv
, h
))
1064 addr_dup_check
(struct addresslist
*al
, struct address
*h
, const char *new_srv
,
1065 const char *other_srv
)
1069 char buf
[INET6_ADDRSTRLEN
];
1070 const char *addrstr
;
1072 TAILQ_FOREACH
(a
, al
, entry
) {
1073 if
(a
->ai_family
!= h
->ai_family ||
1074 a
->ai_socktype
!= h
->ai_socktype ||
1075 a
->ai_protocol
!= h
->ai_protocol ||
1076 a
->slen
!= h
->slen ||
1077 memcmp
(&a
->ss
, &h
->ss
, a
->slen
) != 0)
1080 switch
(h
->ss.ss_family
) {
1082 ia
= &((struct sockaddr_in
*)(&h
->ss
))->sin_addr
;
1085 ia
= &((struct sockaddr_in6
*)(&h
->ss
))->sin6_addr
;
1088 yyerror("unknown address family: %d", h
->ss.ss_family
);
1091 addrstr
= inet_ntop
(h
->ss.ss_family
, ia
, buf
, sizeof
(buf
));
1094 yyerror("server %s: duplicate fcgi listen "
1095 "address %s:%d, already used by server %s",
1096 new_srv
, addrstr
, h
->port
, other_srv
);
1098 log_warnx
("server: %s: duplicate fcgi listen "
1099 "address %s:%d", new_srv
, addrstr
, h
->port
);
1103 yyerror("server: %s: duplicate fcgi listen "
1104 "address, already used by server %s",
1105 new_srv
, other_srv
);
1107 log_warnx
("server %s: duplicate fcgi listen "
1108 "address", new_srv
);
1119 add_addr
(struct server
*new_srv
, struct address
*h
)
1123 /* Address cannot be shared between different servers. */
1124 TAILQ_FOREACH
(srv
, &gotwebd
->servers
, entry
) {
1127 if
(addr_dup_check
(&srv
->al
, h
, new_srv
->name
, srv
->name
))
1131 /* Tolerate duplicate address lines within the scope of a server. */
1132 if
(addr_dup_check
(&new_srv
->al
, h
, NULL
, NULL
) == 0)
1133 TAILQ_INSERT_TAIL
(&new_srv
->al
, h
, entry
);