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