1 /* Test for sched_getaffinity and sched_setaffinity, PID version.
2    Copyright (C) 2015-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* Function definitions for the benefit of tst-skeleton-affinity.c.
20    This variant forks a child process which then invokes
21    sched_getaffinity and sched_setaffinity on the parent PID.  */
22 
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <sched.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31 
32 static int
write_fully(int fd,const void * buffer,size_t length)33 write_fully (int fd, const void *buffer, size_t length)
34 {
35   const void *end = buffer + length;
36   while (buffer < end)
37     {
38       ssize_t bytes_written = TEMP_FAILURE_RETRY
39         (write (fd, buffer, end - buffer));
40       if (bytes_written < 0)
41         return -1;
42       if (bytes_written == 0)
43         {
44           errno = ENOSPC;
45           return -1;
46         }
47       buffer += bytes_written;
48     }
49   return 0;
50 }
51 
52 static ssize_t
read_fully(int fd,void * buffer,size_t length)53 read_fully (int fd, void *buffer, size_t length)
54 {
55   const void *start = buffer;
56   const void *end = buffer + length;
57   while (buffer < end)
58     {
59       ssize_t bytes_read = TEMP_FAILURE_RETRY
60         (read (fd, buffer, end - buffer));
61       if (bytes_read < 0)
62         return -1;
63       if (bytes_read == 0)
64         return buffer - start;
65       buffer += bytes_read;
66     }
67   return length;
68 }
69 
70 static int
process_child_response(int * pipes,pid_t child,cpu_set_t * set,size_t size)71 process_child_response (int *pipes, pid_t child,
72                         cpu_set_t *set, size_t size)
73 {
74   close (pipes[1]);
75 
76   int value_from_child;
77   ssize_t bytes_read = read_fully
78     (pipes[0], &value_from_child, sizeof (value_from_child));
79   if (bytes_read < 0)
80     {
81       printf ("error: read from child: %m\n");
82       exit (1);
83     }
84   if (bytes_read != sizeof (value_from_child))
85     {
86       printf ("error: not enough bytes from child: %zd\n", bytes_read);
87       exit (1);
88     }
89   if (value_from_child == 0)
90     {
91       bytes_read = read_fully (pipes[0], set, size);
92       if (bytes_read < 0)
93         {
94           printf ("error: read: %m\n");
95           exit (1);
96         }
97       if (bytes_read != size)
98         {
99           printf ("error: not enough bytes from child: %zd\n", bytes_read);
100           exit (1);
101         }
102     }
103 
104   int status;
105   if (waitpid (child, &status, 0) < 0)
106     {
107       printf ("error: waitpid: %m\n");
108       exit (1);
109     }
110   if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0))
111     {
112       printf ("error: invalid status from : %m\n");
113       exit (1);
114     }
115 
116   close (pipes[0]);
117 
118   if (value_from_child != 0)
119     {
120       errno = value_from_child;
121       return -1;
122     }
123   return 0;
124 }
125 
126 static int
getaffinity(size_t size,cpu_set_t * set)127 getaffinity (size_t size, cpu_set_t *set)
128 {
129   int pipes[2];
130   if (pipe (pipes) < 0)
131     {
132       printf ("error: pipe: %m\n");
133       exit (1);
134     }
135 
136   int ret = fork ();
137   if (ret < 0)
138     {
139       printf ("error: fork: %m\n");
140       exit (1);
141     }
142   if (ret == 0)
143     {
144       /* Child.  */
145       int ret = sched_getaffinity (getppid (), size, set);
146       if (ret < 0)
147         ret = errno;
148       if (write_fully (pipes[1], &ret, sizeof (ret)) < 0
149           || write_fully (pipes[1], set, size) < 0
150           || (ret == 0 && write_fully (pipes[1], set, size) < 0))
151         {
152           printf ("error: write: %m\n");
153           _exit (1);
154         }
155       _exit (0);
156     }
157 
158   /* Parent.  */
159   return process_child_response (pipes, ret, set, size);
160 }
161 
162 static int
setaffinity(size_t size,const cpu_set_t * set)163 setaffinity (size_t size, const cpu_set_t *set)
164 {
165   int pipes[2];
166   if (pipe (pipes) < 0)
167     {
168       printf ("error: pipe: %m\n");
169       exit (1);
170     }
171 
172   int ret = fork ();
173   if (ret < 0)
174     {
175       printf ("error: fork: %m\n");
176       exit (1);
177     }
178   if (ret == 0)
179     {
180       /* Child.  */
181       int ret = sched_setaffinity (getppid (), size, set);
182       if (write_fully (pipes[1], &ret, sizeof (ret)) < 0)
183         {
184           printf ("error: write: %m\n");
185           _exit (1);
186         }
187       _exit (0);
188     }
189 
190   /* Parent.  There is no affinity mask to read from the child, so the
191      size is 0.  */
192   return process_child_response (pipes, ret, NULL, 0);
193 }
194 
195 struct conf;
early_test(struct conf * unused)196 static bool early_test (struct conf *unused)
197 {
198   return true;
199 }
200 
201 #include "tst-skeleton-affinity.c"
202