]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
pinky: promptly diagnose write errors
authorCollin Funk <collin.funk1@gmail.com>
Fri, 5 Dec 2025 06:14:31 +0000 (22:14 -0800)
committerCollin Funk <collin.funk1@gmail.com>
Fri, 5 Dec 2025 23:42:16 +0000 (15:42 -0800)
In some cases 'pinky' could run forever until interrupted:

    $ ln -s /dev/zero ~/.plan
    $ ln -s /dev/zero ~/.project
    $ timeout -v 5 pinky -l collin > /dev/full
    timeout: sending signal TERM to command ‘pinky’

After this change it will exit upon failing to write to standard output:

    $ timeout -v 5 ./src/pinky -l collin > /dev/full
    pinky: write error: No space left on device

* src/pinky.c: Include fadvise.h, filenamecat.h, full-write.h, and
ioblksize.h.
(cat_file): New function.
(print_entry): Check if standard output has it's error flag set after
printing a user entry.
(print_long_entry): Likewise. Use the new cat_file function.
* NEWS: Mention the improvement.

NEWS
src/pinky.c

diff --git a/NEWS b/NEWS
index 127d70ae08a4a6f2b7c7a4fc5dd9821b5f46d277..b283b6908b0908623d09f85b199d1e6a73093b07 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   csplit, ls, and sort, now handle a more complete set of terminating signals.
 
+  'pinky' will now exit immediately upon receiving a write error, which is
+  significant when reading large plan or project files.
+
   'timeout' on Linux will always terminate the child in the case where the
   timeout process itself dies, like when it receives a KILL signal for example.
 
index b49096b35710819ec5a2dc9a389a8530eb56bbc1..6414446bd9926d67989d25829f83fb945fefaf8c 100644 (file)
 #include "system.h"
 
 #include "canon-host.h"
+#include "fadvise.h"
+#include "filenamecat.h"
+#include "full-write.h"
 #include "hard-locale.h"
+#include "ioblksize.h"
 #include "readutmp.h"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
@@ -309,6 +313,36 @@ print_entry (STRUCT_UTMP const *utmp_ent)
 #endif
 
   putchar ('\n');
+
+  if (ferror (stdout))
+    write_error ();
+}
+
+/* If FILE exists in HOME, print it to standard output, preceded by HEADER. */
+
+static void
+cat_file (char const *header, char const *home, char const *file)
+{
+  char *full_name = file_name_concat (home, file, nullptr);
+  int fd = open (full_name, O_RDONLY);
+
+  if (0 <= fd)
+    {
+      idx_t header_len = strlen (header);
+      if (write (STDOUT_FILENO, header, header_len) != header_len)
+        write_error ();
+
+      fdadvise (fd, 0, 0, FADVISE_SEQUENTIAL);
+
+      char buf[IO_BUFSIZE];
+      for (ssize_t bytes_read; 0 < (bytes_read = read (fd, buf, sizeof buf));)
+        if (full_write (STDOUT_FILENO, buf, bytes_read) != bytes_read)
+          write_error ();
+
+      close (fd);
+    }
+
+  free (full_name);
 }
 
 /* Display a verbose line of information about UTMP_ENT. */
@@ -355,54 +389,15 @@ print_long_entry (const char name[])
     }
 
   if (include_project)
-    {
-      FILE *stream;
-      char buf[1024];
-      char const *const baseproject = "/.project";
-      char *const project =
-        xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
-      stpcpy (stpcpy (project, pw->pw_dir), baseproject);
-
-      stream = fopen (project, "r");
-      if (stream)
-        {
-          size_t bytes;
-
-          printf (_("Project: "));
-
-          while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
-            fwrite (buf, 1, bytes, stdout);
-          fclose (stream);
-        }
-
-      free (project);
-    }
+    cat_file (_("Project: "), pw->pw_dir, ".project");
 
   if (include_plan)
-    {
-      FILE *stream;
-      char buf[1024];
-      char const *const baseplan = "/.plan";
-      char *const plan =
-        xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
-      stpcpy (stpcpy (plan, pw->pw_dir), baseplan);
-
-      stream = fopen (plan, "r");
-      if (stream)
-        {
-          size_t bytes;
-
-          printf (_("Plan:\n"));
-
-          while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
-            fwrite (buf, 1, bytes, stdout);
-          fclose (stream);
-        }
-
-      free (plan);
-    }
+    cat_file (_("Plan:\n"), pw->pw_dir, ".plan");
 
   putchar ('\n');
+
+  if (ferror (stdout))
+    write_error ();
 }
 
 /* Print the username of each valid entry and the number of valid entries