about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/tst-setgetname.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@hack.frob.com>2014-07-07 09:28:38 -0700
committerRoland McGrath <roland@hack.frob.com>2014-07-07 09:28:38 -0700
commit08192659bbeae149e7cb1f4c43547257f7099bb0 (patch)
tree9f602a4e9f0aefdb06bc15a76be46e958e91a58f /sysdeps/unix/sysv/linux/tst-setgetname.c
parent96baf6ffc57b811bbc424a1fc72ef77dd56d7a18 (diff)
downloadglibc-08192659bbeae149e7cb1f4c43547257f7099bb0.tar.gz
glibc-08192659bbeae149e7cb1f4c43547257f7099bb0.tar.xz
glibc-08192659bbeae149e7cb1f4c43547257f7099bb0.zip
Get rid of nptl/sysdeps/ entirely!
Diffstat (limited to 'sysdeps/unix/sysv/linux/tst-setgetname.c')
-rw-r--r--sysdeps/unix/sysv/linux/tst-setgetname.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/tst-setgetname.c b/sysdeps/unix/sysv/linux/tst-setgetname.c
new file mode 100644
index 0000000000..f5693e26c4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-setgetname.c
@@ -0,0 +1,315 @@
+/* Test pthread_setname_np and pthread_getname_np.
+   Copyright (C) 2013-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <kernel-features.h>
+
+/* New name of process.  */
+#define NEW_NAME "setname"
+
+/* Name of process which is one byte too big
+   e.g. 17 bytes including null-terminator  */
+#define BIG_NAME       "....V....X....XV"
+
+/* Longest name of a process
+   e.g. 16 bytes including null-terminator.  */
+#define LONGEST_NAME   "....V....X....X"
+
+/* One less than longest name with unique
+   characters to detect modification.  */
+#define CANARY_NAME    "abcdefghijklmn"
+
+/* On Linux the maximum length of the name of a task *including* the null
+   terminator.  */
+#define TASK_COMM_LEN 16
+
+long
+gettid (void)
+{
+    return syscall(__NR_gettid);
+}
+
+/* On Linux we can read this task's name from /proc.  */
+int
+get_self_comm (long tid, char *buf, size_t len)
+{
+  int res = 0;
+#define FMT "/proc/self/task/%lu/comm"
+  char fname[sizeof (FMT) + 8];
+  sprintf (fname, FMT, (unsigned long) tid);
+
+  int fd = open (fname, O_RDONLY);
+  if (fd == -1)
+    return errno;
+
+  ssize_t n = read (fd, (void *) buf, len);
+  if (n < 0)
+    res = errno;
+  else
+    {
+      if (buf[n - 1] == '\n')
+        buf[n - 1] = '\0';
+      else if (n == len)
+        res = ERANGE;
+      else
+        buf[n] = '\0';
+    }
+
+  close (fd);
+  return res;
+}
+
+int
+do_test (int argc, char **argv)
+{
+  pthread_t self;
+  int res;
+  int ret = 0;
+  char name[TASK_COMM_LEN];
+  char name_check[TASK_COMM_LEN];
+
+  memset (name, '\0', TASK_COMM_LEN);
+  memset (name_check, '\0', TASK_COMM_LEN);
+
+  /* Test 1: Get the name of the task via pthread_getname_np and /proc
+     and verify that they both match.  */
+  self = pthread_self ();
+  res = pthread_getname_np (self, name, TASK_COMM_LEN);
+
+  if (res == 0)
+    {
+      res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+
+#if !__ASSUME_PROC_PID_TASK_COMM
+      /* On this first test we look for ENOENT to be returned from
+         get_self_comm to indicate that the kernel is older than
+         2.6.33 and doesn't contain comm within the proc structure.
+         In that case we skip the entire test.  */
+      if (res == ENOENT)
+	{
+	  printf ("SKIP: The kernel does not have /proc/self/task/%%lu/comm.\n");
+	  return 0;
+	}
+#endif
+
+      if (res == 0)
+       {
+         if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
+           printf ("PASS: Test 1 - pthread_getname_np and /proc agree.\n");
+         else
+           {
+             printf ("FAIL: Test 1 - pthread_getname_np and /proc differ"
+                     " i.e. %s != %s\n", name, name_check);
+             ret++;
+           }
+       }
+      else
+       {
+         printf ("FAIL: Test 1 - unable read task name via proc.\n");
+         ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 1 - pthread_getname_np failed with error %d\n", res);
+      ret++;
+    }
+
+  /* Test 2: Test setting the name and then independently verify it
+             was set.  */
+  res = pthread_setname_np (self, NEW_NAME);
+
+  if (res == 0)
+    {
+      res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+      if (res == 0)
+        {
+         if (strncmp (NEW_NAME, name_check, strlen (BIG_NAME)) == 0)
+           printf ("PASS: Test 2 - Value used in pthread_setname_np and"
+                   " /proc agree.\n");
+         else
+           {
+             printf ("FAIL: Test 2 - Value used in pthread_setname_np"
+		     " and /proc differ i.e. %s != %s\n",
+		     NEW_NAME, name_check);
+             ret++;
+           }
+        }
+      else
+       {
+         printf ("FAIL: Test 2 - unable to read task name via proc.\n");
+         ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 2 - pthread_setname_np failed with error %d\n", res);
+      ret++;
+    }
+
+  /* Test 3: Test setting a name that is one-byte too big.  */
+  res = pthread_getname_np (self, name, TASK_COMM_LEN);
+
+  if (res == 0)
+    {
+      res = pthread_setname_np (self, BIG_NAME);
+      if (res != 0)
+        {
+         if (res == ERANGE)
+           {
+             printf ("PASS: Test 3 - pthread_setname_np returned ERANGE"
+                     " for a process name that was too long.\n");
+
+             /* Verify the old name didn't change.  */
+             res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+             if (res == 0)
+               {
+                 if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
+                   printf ("PASS: Test 3 - Original name unchanged after"
+                           " pthread_setname_np returned ERANGE.\n");
+                 else
+                   {
+                     printf ("FAIL: Test 3 - Original name changed after"
+                             " pthread_setname_np returned ERANGE"
+                             " i.e. %s != %s\n",
+                             name, name_check);
+                     ret++;
+                   }
+               }
+             else
+               {
+                 printf ("FAIL: Test 3 - unable to read task name.\n");
+                 ret++;
+               }
+           }
+         else
+           {
+             printf ("FAIL: Test 3 - Wrong error returned"
+		     " i.e. ERANGE != %d\n", res);
+             ret++;
+           }
+        }
+      else
+        {
+         printf ("FAIL: Test 3 - Too-long name accepted by"
+	         " pthread_setname_np.\n");
+         ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 3 - Unable to get original name.\n");
+      ret++;
+    }
+
+  /* Test 4: Verify that setting the longest name works.  */
+  res = pthread_setname_np (self, LONGEST_NAME);
+
+  if (res == 0)
+    {
+      res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+      if (res == 0)
+        {
+         if (strncmp (LONGEST_NAME, name_check, strlen (BIG_NAME)) == 0)
+           printf ("PASS: Test 4 - Longest name set via pthread_setname_np"
+                   " agrees with /proc.\n");
+         else
+           {
+             printf ("FAIL: Test 4 - Value used in pthread_setname_np and /proc"
+		     " differ i.e. %s != %s\n", LONGEST_NAME, name_check);
+             ret++;
+           }
+        }
+      else
+       {
+         printf ("FAIL: Test 4 - unable to read task name via proc.\n");
+         ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 4 - pthread_setname_np failed with error %d\n", res);
+      ret++;
+    }
+
+  /* Test 5: Verify that getting a long name into a small buffer fails.  */
+  strncpy (name, CANARY_NAME, strlen (CANARY_NAME) + 1);
+
+  /* Claim the buffer length is strlen (LONGEST_NAME).  This is one character
+     too small to hold LONGEST_NAME *and* the null terminator.  We should get
+     back ERANGE and name should be unmodified.  */
+  res = pthread_getname_np (self, name, strlen (LONGEST_NAME));
+
+  if (res != 0)
+    {
+      if (res == ERANGE)
+        {
+	  if (strncmp (CANARY_NAME, name, strlen (BIG_NAME)) == 0)
+	    {
+	      printf ("PASS: Test 5 - ERANGE and buffer unmodified.\n");
+	    }
+	  else
+	    {
+	      printf ("FAIL: Test 5 - Original buffer modified.\n");
+	      ret++;
+	    }
+        }
+      else
+        {
+	  printf ("FAIL: Test 5 - Did not return ERANGE for small buffer.\n");
+	  ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 5 - Returned name longer than buffer.\n");
+      ret++;
+    }
+
+  /* Test 6: Lastly make sure we can read back the longest name.  */
+  res = pthread_getname_np (self, name, strlen (LONGEST_NAME) + 1);
+
+  if (res == 0)
+    {
+      if (strncmp (LONGEST_NAME, name, strlen (BIG_NAME)) == 0)
+        {
+	  printf ("PASS: Test 6 - Read back longest name correctly.\n");
+        }
+      else
+        {
+	  printf ("FAIL: Test 6 - Read \"%s\" instead of longest name.\n",
+		  name);
+	  ret++;
+        }
+    }
+  else
+    {
+      printf ("FAIL: Test 6 - pthread_getname_np failed with error %d\n", res);
+      ret++;
+    }
+
+  return ret;
+}
+
+#include <test-skeleton.c>