1 /* Copyright (C) 2001-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <errno.h>
19 #include <netdb.h>
20 #include <pthread.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include <gai_misc.h>
25
26
27 /* We need this special structure to handle asynchronous I/O. */
28 struct async_waitlist
29 {
30 unsigned int counter;
31 struct sigevent sigev;
32 struct waitlist list[0];
33 };
34
35
36 int
__getaddrinfo_a(int mode,struct gaicb * list[],int ent,struct sigevent * sig)37 __getaddrinfo_a (int mode, struct gaicb *list[], int ent, struct sigevent *sig)
38 {
39 struct sigevent defsigev;
40 struct requestlist *requests[ent];
41 int cnt;
42 volatile unsigned int total = 0;
43 int result = 0;
44
45 /* Check arguments. */
46 if (mode != GAI_WAIT && mode != GAI_NOWAIT)
47 {
48 __set_errno (EINVAL);
49 return EAI_SYSTEM;
50 }
51
52 if (sig == NULL)
53 {
54 defsigev.sigev_notify = SIGEV_NONE;
55 sig = &defsigev;
56 }
57
58 /* Request the mutex. */
59 __pthread_mutex_lock (&__gai_requests_mutex);
60
61 /* Now we can enqueue all requests. Since we already acquired the
62 mutex the enqueue function need not do this. */
63 for (cnt = 0; cnt < ent; ++cnt)
64 if (list[cnt] != NULL)
65 {
66 requests[cnt] = __gai_enqueue_request (list[cnt]);
67
68 if (requests[cnt] != NULL)
69 /* Successfully enqueued. */
70 ++total;
71 else
72 /* Signal that we've seen an error. `errno' and the error code
73 of the gaicb will tell more. */
74 result = EAI_SYSTEM;
75 }
76 else
77 requests[cnt] = NULL;
78
79 if (total == 0)
80 {
81 /* We don't have anything to do except signalling if we work
82 asynchronously. */
83
84 /* Release the mutex. We do this before raising a signal since the
85 signal handler might do a `siglongjmp' and then the mutex is
86 locked forever. */
87 __pthread_mutex_unlock (&__gai_requests_mutex);
88
89 if (mode == GAI_NOWAIT)
90 __gai_notify_only (sig,
91 sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0);
92
93 return result;
94 }
95 else if (mode == GAI_WAIT)
96 {
97 #ifndef DONT_NEED_GAI_MISC_COND
98 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
99 #endif
100 struct waitlist waitlist[ent];
101 int oldstate;
102
103 total = 0;
104 for (cnt = 0; cnt < ent; ++cnt)
105 if (requests[cnt] != NULL)
106 {
107 #ifndef DONT_NEED_GAI_MISC_COND
108 waitlist[cnt].cond = &cond;
109 #endif
110 waitlist[cnt].next = requests[cnt]->waiting;
111 waitlist[cnt].counterp = &total;
112 waitlist[cnt].sigevp = NULL;
113 waitlist[cnt].caller_pid = 0; /* Not needed. */
114 requests[cnt]->waiting = &waitlist[cnt];
115 ++total;
116 }
117
118 /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
119 points we must be careful. We added entries to the waiting lists
120 which we must remove. So defer cancelation for now. */
121 __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
122
123 while (total > 0)
124 {
125 #ifdef DONT_NEED_GAI_MISC_COND
126 int not_used __attribute__ ((unused));
127 GAI_MISC_WAIT (not_used, total, NULL, 1);
128 #else
129 pthread_cond_wait (&cond, &__gai_requests_mutex);
130 #endif
131 }
132
133 /* Now it's time to restore the cancelation state. */
134 __pthread_setcancelstate (oldstate, NULL);
135
136 #ifndef DONT_NEED_GAI_MISC_COND
137 /* Release the conditional variable. */
138 if (pthread_cond_destroy (&cond) != 0)
139 /* This must never happen. */
140 abort ();
141 #endif
142 }
143 else
144 {
145 struct async_waitlist *waitlist;
146
147 waitlist = (struct async_waitlist *)
148 malloc (sizeof (struct async_waitlist)
149 + (ent * sizeof (struct waitlist)));
150
151 if (waitlist == NULL)
152 result = EAI_AGAIN;
153 else
154 {
155 pid_t caller_pid = sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0;
156 total = 0;
157
158 for (cnt = 0; cnt < ent; ++cnt)
159 if (requests[cnt] != NULL)
160 {
161 #ifndef DONT_NEED_GAI_MISC_COND
162 waitlist->list[cnt].cond = NULL;
163 #endif
164 waitlist->list[cnt].next = requests[cnt]->waiting;
165 waitlist->list[cnt].counterp = &waitlist->counter;
166 waitlist->list[cnt].sigevp = &waitlist->sigev;
167 waitlist->list[cnt].caller_pid = caller_pid;
168 requests[cnt]->waiting = &waitlist->list[cnt];
169 ++total;
170 }
171
172 waitlist->counter = total;
173 waitlist->sigev = *sig;
174 }
175 }
176
177 /* Release the mutex. */
178 __pthread_mutex_unlock (&__gai_requests_mutex);
179
180 return result;
181 }
182 #if PTHREAD_IN_LIBC
183 versioned_symbol (libc, __getaddrinfo_a, getaddrinfo_a, GLIBC_2_34);
184
185 # if OTHER_SHLIB_COMPAT (libanl, GLIBC_2_2_3, GLIBC_2_34)
186 compat_symbol (libanl, __getaddrinfo_a, getaddrinfo_a, GLIBC_2_2_3);
187 # endif
188 #else /* !PTHREAD_IN_LIBC */
189 strong_alias (__getaddrinfo_a, getaddrinfo_a)
190 #endif /* !PTHREAD_IN_LIBC */
191