1 /* Test pthread_setname_np and pthread_getname_np.
2    Copyright (C) 2013-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 License as
7    published by the Free Software Foundation; either version 2.1 of the
8    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; see the file COPYING.LIB.  If
17    not, see <https://www.gnu.org/licenses/>.  */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <pthread.h>
21 #include <string.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 
27 /* New name of process.  */
28 #define NEW_NAME "setname"
29 
30 /* Name of process which is one byte too big
31    e.g. 17 bytes including null-terminator  */
32 #define BIG_NAME       "....V....X....XV"
33 
34 /* Longest name of a process
35    e.g. 16 bytes including null-terminator.  */
36 #define LONGEST_NAME   "....V....X....X"
37 
38 /* One less than longest name with unique
39    characters to detect modification.  */
40 #define CANARY_NAME    "abcdefghijklmn"
41 
42 /* On Linux the maximum length of the name of a task *including* the null
43    terminator.  */
44 #define TASK_COMM_LEN 16
45 
46 /* On Linux we can read this task's name from /proc.  */
47 int
get_self_comm(long tid,char * buf,size_t len)48 get_self_comm (long tid, char *buf, size_t len)
49 {
50   int res = 0;
51 #define FMT "/proc/self/task/%lu/comm"
52   char fname[sizeof (FMT) + 32];
53   sprintf (fname, FMT, (unsigned long) tid);
54 
55   int fd = open (fname, O_RDONLY);
56   if (fd == -1)
57     return errno;
58 
59   ssize_t n = read (fd, (void *) buf, len);
60   if (n < 0)
61     res = errno;
62   else
63     {
64       if (buf[n - 1] == '\n')
65         buf[n - 1] = '\0';
66       else if (n == len)
67         res = ERANGE;
68       else
69         buf[n] = '\0';
70     }
71 
72   close (fd);
73   return res;
74 }
75 
76 int
do_test(int argc,char ** argv)77 do_test (int argc, char **argv)
78 {
79   pthread_t self;
80   int res;
81   int ret = 0;
82   char name[TASK_COMM_LEN];
83   char name_check[TASK_COMM_LEN];
84 
85   memset (name, '\0', TASK_COMM_LEN);
86   memset (name_check, '\0', TASK_COMM_LEN);
87 
88   /* Test 1: Get the name of the task via pthread_getname_np and /proc
89      and verify that they both match.  */
90   self = pthread_self ();
91   res = pthread_getname_np (self, name, TASK_COMM_LEN);
92 
93   if (res == 0)
94     {
95       res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
96 
97       if (res == 0)
98        {
99          if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
100            printf ("PASS: Test 1 - pthread_getname_np and /proc agree.\n");
101          else
102            {
103              printf ("FAIL: Test 1 - pthread_getname_np and /proc differ"
104                      " i.e. %s != %s\n", name, name_check);
105              ret++;
106            }
107        }
108       else
109        {
110          printf ("FAIL: Test 1 - unable read task name via proc.\n");
111          ret++;
112         }
113     }
114   else
115     {
116       printf ("FAIL: Test 1 - pthread_getname_np failed with error %d\n", res);
117       ret++;
118     }
119 
120   /* Test 2: Test setting the name and then independently verify it
121              was set.  */
122   res = pthread_setname_np (self, NEW_NAME);
123 
124   if (res == 0)
125     {
126       res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
127       if (res == 0)
128         {
129          if (strncmp (NEW_NAME, name_check, strlen (BIG_NAME)) == 0)
130            printf ("PASS: Test 2 - Value used in pthread_setname_np and"
131                    " /proc agree.\n");
132          else
133            {
134              printf ("FAIL: Test 2 - Value used in pthread_setname_np"
135 		     " and /proc differ i.e. %s != %s\n",
136 		     NEW_NAME, name_check);
137              ret++;
138            }
139         }
140       else
141        {
142          printf ("FAIL: Test 2 - unable to read task name via proc.\n");
143          ret++;
144         }
145     }
146   else
147     {
148       printf ("FAIL: Test 2 - pthread_setname_np failed with error %d\n", res);
149       ret++;
150     }
151 
152   /* Test 3: Test setting a name that is one-byte too big.  */
153   res = pthread_getname_np (self, name, TASK_COMM_LEN);
154 
155   if (res == 0)
156     {
157       res = pthread_setname_np (self, BIG_NAME);
158       if (res != 0)
159         {
160          if (res == ERANGE)
161            {
162              printf ("PASS: Test 3 - pthread_setname_np returned ERANGE"
163                      " for a process name that was too long.\n");
164 
165              /* Verify the old name didn't change.  */
166              res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
167              if (res == 0)
168                {
169                  if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
170                    printf ("PASS: Test 3 - Original name unchanged after"
171                            " pthread_setname_np returned ERANGE.\n");
172                  else
173                    {
174                      printf ("FAIL: Test 3 - Original name changed after"
175                              " pthread_setname_np returned ERANGE"
176                              " i.e. %s != %s\n",
177                              name, name_check);
178                      ret++;
179                    }
180                }
181              else
182                {
183                  printf ("FAIL: Test 3 - unable to read task name.\n");
184                  ret++;
185                }
186            }
187          else
188            {
189              printf ("FAIL: Test 3 - Wrong error returned"
190 		     " i.e. ERANGE != %d\n", res);
191              ret++;
192            }
193         }
194       else
195         {
196          printf ("FAIL: Test 3 - Too-long name accepted by"
197 	         " pthread_setname_np.\n");
198          ret++;
199         }
200     }
201   else
202     {
203       printf ("FAIL: Test 3 - Unable to get original name.\n");
204       ret++;
205     }
206 
207   /* Test 4: Verify that setting the longest name works.  */
208   res = pthread_setname_np (self, LONGEST_NAME);
209 
210   if (res == 0)
211     {
212       res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
213       if (res == 0)
214         {
215          if (strncmp (LONGEST_NAME, name_check, strlen (BIG_NAME)) == 0)
216            printf ("PASS: Test 4 - Longest name set via pthread_setname_np"
217                    " agrees with /proc.\n");
218          else
219            {
220              printf ("FAIL: Test 4 - Value used in pthread_setname_np and /proc"
221 		     " differ i.e. %s != %s\n", LONGEST_NAME, name_check);
222              ret++;
223            }
224         }
225       else
226        {
227          printf ("FAIL: Test 4 - unable to read task name via proc.\n");
228          ret++;
229         }
230     }
231   else
232     {
233       printf ("FAIL: Test 4 - pthread_setname_np failed with error %d\n", res);
234       ret++;
235     }
236 
237   /* Test 5: Verify that getting a long name into a small buffer fails.  */
238   strncpy (name, CANARY_NAME, strlen (CANARY_NAME) + 1);
239 
240   /* Claim the buffer length is strlen (LONGEST_NAME).  This is one character
241      too small to hold LONGEST_NAME *and* the null terminator.  We should get
242      back ERANGE and name should be unmodified.  */
243   res = pthread_getname_np (self, name, strlen (LONGEST_NAME));
244 
245   if (res != 0)
246     {
247       if (res == ERANGE)
248         {
249 	  if (strncmp (CANARY_NAME, name, strlen (BIG_NAME)) == 0)
250 	    {
251 	      printf ("PASS: Test 5 - ERANGE and buffer unmodified.\n");
252 	    }
253 	  else
254 	    {
255 	      printf ("FAIL: Test 5 - Original buffer modified.\n");
256 	      ret++;
257 	    }
258         }
259       else
260         {
261 	  printf ("FAIL: Test 5 - Did not return ERANGE for small buffer.\n");
262 	  ret++;
263         }
264     }
265   else
266     {
267       printf ("FAIL: Test 5 - Returned name longer than buffer.\n");
268       ret++;
269     }
270 
271   /* Test 6: Lastly make sure we can read back the longest name.  */
272   res = pthread_getname_np (self, name, strlen (LONGEST_NAME) + 1);
273 
274   if (res == 0)
275     {
276       if (strncmp (LONGEST_NAME, name, strlen (BIG_NAME)) == 0)
277         {
278 	  printf ("PASS: Test 6 - Read back longest name correctly.\n");
279         }
280       else
281         {
282 	  printf ("FAIL: Test 6 - Read \"%s\" instead of longest name.\n",
283 		  name);
284 	  ret++;
285         }
286     }
287   else
288     {
289       printf ("FAIL: Test 6 - pthread_getname_np failed with error %d\n", res);
290       ret++;
291     }
292 
293   return ret;
294 }
295 
296 #include <test-skeleton.c>
297