Skip to content

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Aug 11, 2025

I've optimized the algorithm in several ways (both performance and readability):

  • If there's a match-by-pid, we don't spend time copying a match-by-line, which is considered worse.
  • By using the stack, we don't have superfluous allocations.
  • The enum and the ordered levels of matching make the algorithm much more readable than it originally was.

Reported-by: @Karlson2k

Queued after #1296 .


Revisions:

v2
$ git range-diff shadow/master gh/ut  ut
1:  722a3baa = 1:  722a3baa lib/utmp.c: get_current_utmp(): Rename local variable
2:  34b2ed3b = 2:  34b2ed3b lib/utmp.c: get_current_utmp(): Refactor conditional
3:  8d47dcb6 = 3:  8d47dcb6 lib/utmp.c: get_current_utmp(): Use an enum for readability
4:  9e8fd525 = 4:  9e8fd525 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
-:  -------- > 5:  9a3e362e lib/string/strdup/: memdup(), MEMDUP(): Add APIs
-:  -------- > 6:  1e72a0b8 lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
-:  -------- > 7:  eb01f5ac lib/utmp.c: get_current_utmp(): Use MEMDUP() instead of its pattern
-:  -------- > 8:  9f55b18b lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v2b
  • Rebase
$ git rd 
1:  722a3baa = 1:  4750a0d2 lib/utmp.c: get_current_utmp(): Rename local variable
2:  34b2ed3b = 2:  d6d8b16f lib/utmp.c: get_current_utmp(): Refactor conditional
3:  8d47dcb6 = 3:  544d7b16 lib/utmp.c: get_current_utmp(): Use an enum for readability
4:  9e8fd525 = 4:  1c8dc8d1 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
5:  9a3e362e = 5:  8624ca55 lib/string/strdup/: memdup(), MEMDUP(): Add APIs
6:  1e72a0b8 = 6:  36941b6b lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
7:  eb01f5ac = 7:  e1a28feb lib/utmp.c: get_current_utmp(): Use MEMDUP() instead of its pattern
8:  9f55b18b = 8:  791e81b9 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v2c
  • Rebase
$ git rd 
1:  4750a0d2 = 1:  c5b432f6 lib/utmp.c: get_current_utmp(): Rename local variable
2:  d6d8b16f = 2:  0d98f40b lib/utmp.c: get_current_utmp(): Refactor conditional
3:  544d7b16 = 3:  38661891 lib/utmp.c: get_current_utmp(): Use an enum for readability
4:  1c8dc8d1 = 4:  eb3f0659 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
5:  8624ca55 = 5:  c1820b1b lib/string/strdup/: memdup(), MEMDUP(): Add APIs
6:  36941b6b = 6:  6d10069a lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
7:  e1a28feb = 7:  b9702a86 lib/utmp.c: get_current_utmp(): Use MEMDUP() instead of its pattern
8:  791e81b9 = 8:  7e0ea2a8 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v2d
  • Rebase
$ git rd 
1:  c5b432f6 = 1:  2ac8ca73 lib/utmp.c: get_current_utmp(): Rename local variable
2:  0d98f40b = 2:  e3c226af lib/utmp.c: get_current_utmp(): Refactor conditional
3:  38661891 = 3:  a210a977 lib/utmp.c: get_current_utmp(): Use an enum for readability
4:  eb3f0659 = 4:  a0b06c75 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
5:  c1820b1b ! 5:  e96d999c lib/string/strdup/: memdup(), MEMDUP(): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
        string/strcpy/strtcpy.h \
     +  string/strdup/memdup.c \
     +  string/strdup/memdup.h \
    +   string/strdup/strdup.c \
    +   string/strdup/strdup.h \
        string/strdup/strndupa.c \
    -   string/strdup/strndupa.h \
    -   string/strdup/xstrdup.c \
     
      ## lib/string/strdup/memdup.c (new) ##
     @@
6:  6d10069a ! 6:  c67333a5 lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
    @@ Commit message
         Signed-off-by: Alejandro Colomar <[email protected]>
     
      ## lib/utmp.c ##
    -@@
    - #include <string.h>
    - #include <fcntl.h>
    - 
    -+#include "alloc/malloc.h"
    - #include "alloc/x/xcalloc.h"
    --#include "alloc/x/xmalloc.h"
    - #include "attr.h"
    - #include "sizeof.h"
    - #include "string/strchr/strnul.h"
     @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
        }
      
7:  b9702a86 ! 7:  832bfc52 lib/utmp.c: get_current_utmp(): Use MEMDUP() instead of its pattern
    @@ Commit message
     
      ## lib/utmp.c ##
     @@
    - #include <string.h>
      #include <fcntl.h>
      
    + #include "alloc/calloc.h"
     -#include "alloc/malloc.h"
    - #include "alloc/x/xcalloc.h"
      #include "attr.h"
      #include "sizeof.h"
    + #include "string/strchr/strnul.h"
     @@
      #include "string/strcmp/strprefix.h"
      #include "string/strcpy/strncpy.h"
      #include "string/strcpy/strtcpy.h"
     +#include "string/strdup/memdup.h"
    - #include "string/strdup/xstrdup.h"
    - #include "string/strdup/xstrndup.h"
    + #include "string/strdup/strdup.h"
    + #include "string/strdup/strndup.h"
      
     @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
                }
8:  7e0ea2a8 = 8:  a9f85f1b lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v3
$ git range-diff shadow/master..gh/ut gh/memdup..ut 
1:  2ac8ca73c < -:  --------- lib/utmp.c: get_current_utmp(): Rename local variable
2:  e3c226af5 = 1:  84d29f190 lib/utmp.c: get_current_utmp(): Refactor conditional
3:  a210a9773 = 2:  579b7aaa4 lib/utmp.c: get_current_utmp(): Use an enum for readability
4:  a0b06c75e ! 3:  ecee78c80 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
      
     -  if (NULL == ut)
     -          ut = ut_by_pid ?: ut_by_line;
    --
    --  if (NULL != ut) {
    --          struct utmpx  *dup;
    --
    --          dup = XMALLOC(1, struct utmpx);
    --          memcpy(dup, ut, sizeof(*ut));
    --          ut = dup;
    -+  if (match) {
    -+          ut = XMALLOC(1, struct utmpx);
    -+          *ut = ut_copy;
    -+  } else {
    -+          ut = NULL;
    -   }
    ++  ut = match ? MEMDUP(&ut_copy, struct utmpx) : NULL;
      
    +-  if (NULL != ut)
    +-          ut = MEMDUP(ut, struct utmpx);
    +-
     -  free(ut_by_line);
     -  free(ut_by_pid);
        endutxent();
5:  e96d999c3 < -:  --------- lib/string/strdup/: memdup(), MEMDUP(): Add APIs
6:  c67333a54 < -:  --------- lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
7:  832bfc52f < -:  --------- lib/utmp.c: get_current_utmp(): Use MEMDUP() instead of its pattern
8:  a9f85f1b1 = 4:  7c64a9b26 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
$ git diff gh/ut..ut 
diff --git a/lib/string/strdup/memdup.h b/lib/string/strdup/memdup.h
index df21d624b..cff575b9a 100644
--- a/lib/string/strdup/memdup.h
+++ b/lib/string/strdup/memdup.h
@@ -22,7 +22,7 @@ ATTR_MALLOC(free)
 inline void *memdup(const void *p, size_t size);
 
 
-// memory duplicate
+// memdup - memory duplicate
 inline void *
 memdup(const void *p, size_t size)
 {
diff --git a/lib/utmp.c b/lib/utmp.c
index a965bfa1d..7c0c1cc10 100644
--- a/lib/utmp.c
+++ b/lib/utmp.c
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 
 #include "alloc/calloc.h"
+#include "alloc/malloc.h"
 #include "attr.h"
 #include "sizeof.h"
 #include "string/strchr/strnul.h"
v3b
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  84d29f190 = 1:  52fd9f2ee lib/utmp.c: get_current_utmp(): Refactor conditional
2:  579b7aaa4 = 2:  d155aaf74 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  ecee78c80 = 3:  ed88a77dd lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  7c64a9b26 = 4:  0a76cb494 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v3c
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  52fd9f2ee = 1:  e70da87fe lib/utmp.c: get_current_utmp(): Refactor conditional
2:  d155aaf74 = 2:  0bcdc48fb lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  ed88a77dd = 3:  16bb342f7 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  0a76cb494 = 4:  7956194df lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v4
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  e70da87fe = 1:  43fc5699e lib/utmp.c: get_current_utmp(): Refactor conditional
2:  0bcdc48fb = 2:  159e6005f lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  16bb342f7 = 3:  32babe5ce lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  7956194df = 4:  cf6bb3530 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v4b
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  43fc5699e = 1:  25748a7ed lib/utmp.c: get_current_utmp(): Refactor conditional
2:  159e6005f = 2:  ee9a8487e lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  32babe5ce = 3:  c6aaad6fa lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  cf6bb3530 = 4:  7ec1305db lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
v5
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  25748a7ed ! 1:  dc6f979dd lib/utmp.c: get_current_utmp(): Refactor conditional
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
     -          } else if (   (NULL == ut_by_line)
     -                     && (LOGIN_PROCESS == ut->ut_type) /* Be more picky when matching by 'ut_line' only */
     -                     && (is_my_tty(ut->ut_line))) {
    --                  ut_by_line = XMALLOC(1, struct utmpx);
    +-                  ut_by_line = xmalloc_T(1, struct utmpx);
     -                  *ut_by_line = *ut;
     +          } else if (   LOGIN_PROCESS == ut->ut_type /* Be more picky when matching by 'ut_line' only */
     +                     && is_my_tty(ut->ut_line))
     +          {
     +                  if (NULL == ut_by_line) {
    -+                          ut_by_line = XMALLOC(1, struct utmpx);
    ++                          ut_by_line = xmalloc_T(1, struct utmpx);
     +                          *ut_by_line = *ut;
     +                  }
                }
2:  ee9a8487e ! 2:  0b5030279 lib/utmp.c: get_current_utmp(): Use an enum for readability
    @@ lib/utmp.c: ATTR_MALLOC(free)
     -                  if (NULL == ut_by_pid) {
     +                  if (match < UT_MATCH_BY_PID) {
     +                          match = UT_MATCH_BY_PID;
    -                           ut_by_pid = XMALLOC(1, struct utmpx);
    +                           ut_by_pid = xmalloc_T(1, struct utmpx);
                                *ut_by_pid = *ut;
                        }
     @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
     -                  if (NULL == ut_by_line) {
     +                  if (match < UT_MATCH_BY_LINE) {
     +                          match = UT_MATCH_BY_LINE;
    -                           ut_by_line = XMALLOC(1, struct utmpx);
    +                           ut_by_line = xmalloc_T(1, struct utmpx);
                                *ut_by_line = *ut;
                        }
3:  c6aaad6fa ! 3:  68e1abc57 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
      
                        if (match < UT_MATCH_BY_PID) {
                                match = UT_MATCH_BY_PID;
    --                          ut_by_pid = XMALLOC(1, struct utmpx);
    +-                          ut_by_pid = xmalloc_T(1, struct utmpx);
     -                          *ut_by_pid = *ut;
     +                          ut_copy = *ut;
                        }
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
                {
                        if (match < UT_MATCH_BY_LINE) {
                                match = UT_MATCH_BY_LINE;
    --                          ut_by_line = XMALLOC(1, struct utmpx);
    +-                          ut_by_line = xmalloc_T(1, struct utmpx);
     -                          *ut_by_line = *ut;
     +                          ut_copy = *ut;
                        }
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
      
     -  if (NULL == ut)
     -          ut = ut_by_pid ?: ut_by_line;
    -+  ut = match ? MEMDUP(&ut_copy, struct utmpx) : NULL;
    ++  ut = match ? memdup_T(&ut_copy, struct utmpx) : NULL;
      
     -  if (NULL != ut)
    --          ut = MEMDUP(ut, struct utmpx);
    +-          ut = memdup_T(ut, struct utmpx);
     -
     -  free(ut_by_line);
     -  free(ut_by_pid);
4:  7ec1305db ! 4:  b8338efc1 lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
    @@ Metadata
     Author: Alejandro Colomar <[email protected]>
     
      ## Commit message ##
    -    lib/utmp.c: get_current_utmp(): Move MEMDUP() to the return statement
    +    lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
     
         This removes the reuse of 'ut', with which I wasn't entirely happy.
         It also reduces the scope between setutxent()/endutxent().
    @@ lib/utmp.c: get_current_utmp(pid_t main_pid)
                }
        }
      
    --  ut = match ? MEMDUP(&ut_copy, struct utmpx) : NULL;
    +-  ut = match ? memdup_T(&ut_copy, struct utmpx) : NULL;
     -
        endutxent();
      
     -  return ut;
    -+  return match ? MEMDUP(&ut_copy, struct utmpx) : NULL;
    ++  return match ? memdup_T(&ut_copy, struct utmpx) : NULL;
      }
      
      
v5b
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  dc6f979dd = 1:  cc02a4be8 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  0b5030279 = 2:  ec17eaf5d lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  68e1abc57 = 3:  509730966 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  b8338efc1 = 4:  cd921007f lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5c
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  cc02a4be8 = 1:  011c700e6 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  ec17eaf5d = 2:  9f990fe97 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  509730966 = 3:  158b38e49 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  cd921007f = 4:  8ebd37062 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5d
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  011c700e6 = 1:  e92dc65b4 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  9f990fe97 = 2:  37f5e7c82 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  158b38e49 = 3:  a58c4bc9d lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  8ebd37062 = 4:  1b33b6b07 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5e
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  e92dc65b4 = 1:  74a691810 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  37f5e7c82 = 2:  335118a2d lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  a58c4bc9d = 3:  650097803 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  1b33b6b07 = 4:  cf983b2ed lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5f
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  74a691810 = 1:  ee403fb6d lib/utmp.c: get_current_utmp(): Refactor conditional
2:  335118a2d = 2:  2eb6e936c lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  650097803 = 3:  2a945a205 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  cf983b2ed = 4:  051312b3b lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5g
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  ee403fb6d = 1:  d3c076a7e lib/utmp.c: get_current_utmp(): Refactor conditional
2:  2eb6e936c = 2:  93a5ecd45 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  2a945a205 = 3:  3bc4fcd5a lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  051312b3b = 4:  a090ce529 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5h
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  d3c076a7e = 1:  6f81847aa lib/utmp.c: get_current_utmp(): Refactor conditional
2:  93a5ecd45 = 2:  784ee759b lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  3bc4fcd5a = 3:  ac6b7c299 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  a090ce529 = 4:  046d28e4b lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5i
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  6f81847aa = 1:  462661319 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  784ee759b = 2:  ac7b22421 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  ac6b7c299 = 3:  0913b7ec1 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  046d28e4b = 4:  f785ceb79 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5j
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  462661319 = 1:  d70af3c83 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  ac7b22421 = 2:  a51498918 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  0913b7ec1 = 3:  140742c61 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  f785ceb79 = 4:  8a0223b3a lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5k
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  d70af3c83 = 1:  32c3ab1a6 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  a51498918 = 2:  cd4c8642a lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  140742c61 = 3:  00625f45e lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  8a0223b3a = 4:  8e4f43ff2 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5l
  • Rebase
$ git range-diff gh/T..gh/memdup T..memdup 
1:  d2fb0444f = 1:  e4f14282f lib/string/strdup/: memdup{,_T}(): Add APIs
2:  ee58b30d6 = 2:  fa7692fd5 lib/utmp.c: get_current_utmp(): Don't exit(3) from library code
3:  94b4a8d61 = 3:  1cf10a6c6 lib/utmp.c: get_current_utmp(): Use memdup_T() instead of its pattern
v5m
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  7d88f93f5 = 1:  8a0c113e2 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  5bff54317 = 2:  9cfda2ff0 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  77ccdecba = 3:  09a2600b2 lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  b1a45f3a6 = 4:  38a366138 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement
v5n
  • Rebase
$ git range-diff gh/memdup..gh/ut memdup..ut 
1:  8a0c113e2 = 1:  c9dcf0f46 lib/utmp.c: get_current_utmp(): Refactor conditional
2:  9cfda2ff0 = 2:  77ed22d70 lib/utmp.c: get_current_utmp(): Use an enum for readability
3:  09a2600b2 = 3:  b9cb86d1c lib/utmp.c: get_current_utmp(): Use the stack for temporary copies
4:  38a366138 = 4:  0a6b01d69 lib/utmp.c: get_current_utmp(): Move memdup_T() to the return statement

@alejandro-colomar alejandro-colomar changed the title ut match algorithm improvements. ut-match algorithm improvements. Aug 11, 2025
@alejandro-colomar alejandro-colomar marked this pull request as ready for review August 11, 2025 12:34
Copy link
Contributor

@Karlson2k Karlson2k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one looks fine

Copy link
Contributor

@Karlson2k Karlson2k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one looks bad.
Separating conditions by moving them to the nested "if()" does not improve readability.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Aug 11, 2025

This one looks bad. Separating conditions by moving them to the nested "if()" does not improve readability.

IMO, it's more readable in this specific case, because it aligns the similar conditions. It also makes the other commits smaller.

(In fact, it made me finally fully understand it, which I hadn't understood from the previous implementation.)

@Karlson2k
Copy link
Contributor

Karlson2k commented Aug 11, 2025

This one looks bad. Separating conditions by moving them to the nested "if()" does not improve readability.

IMO, it's more readable in this specific case, because it aligns the similar conditions. It also makes the other commits smaller.

"Smaller commits" should not be a reason for "worse code".
Try with the initial order or conditions and enum check at the first place.
It is even more readable then this.

		} else if (   (match < UT_MATCH_BY_LINE)
			   && (LOGIN_PROCESS == ut->ut_type) /* Be more picky when matching by 'ut_line' only */
			   && is_my_tty(ut->ut_line))
		{
			match = UT_MATCH_BY_LINE;
			ut_copy = *ut;
		}

vs

		} else if (   LOGIN_PROCESS == ut->ut_type /* Be more picky when matching by 'ut_line' only */
			   && is_my_tty(ut->ut_line)) {
			if (match < UT_MATCH_BY_LINE)
			{
				match = UT_MATCH_BY_LINE;
				ut_copy = *ut;
			}
		}

Simpler, shorter, better and more efficient.

Copy link
Contributor

@Karlson2k Karlson2k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested if() should be avoided.

@alejandro-colomar
Copy link
Collaborator Author

This one looks bad. Separating conditions by moving them to the nested "if()" does not improve readability.

IMO, it's more readable in this specific case, because it aligns the similar conditions. It also makes the other commits smaller.

"Smaller commits" should not be a reason for "worse code". Try with the initial order or conditions and enum check at the first place. It is even more readable then this.

		} else if (   (match < UT_MATCH_BY_LINE)
			   && (LOGIN_PROCESS == ut->ut_type) /* Be more picky when matching by 'ut_line' only */
			   && is_my_tty(ut->ut_line))
		{
			match = UT_MATCH_BY_LINE;
			ut_copy = *ut;
		}

vs

		} else if (   LOGIN_PROCESS == ut->ut_type /* Be more picky when matching by 'ut_line' only */
			   && is_my_tty(ut->ut_line)) {
			if (match < UT_MATCH_BY_LINE)
			{
				match = UT_MATCH_BY_LINE;
				ut_copy = *ut;
			}
		}

Simpler, shorter, better

I disagree.

I like much better the latter (but I'd fix the braces). Both UT_MATCH_BY_LINE appear together, which makes more sense. It is also more consistent with the other uses of the enumerators in the loop.

and more efficient.

@Karlson2k
Copy link
Contributor

Karlson2k commented Aug 11, 2025

Besides inconsistent styles, questionable readability improvements in some places, the code is fine.
It should work.
A single enum variable is better then two boolean variables.

@alejandro-colomar alejandro-colomar force-pushed the ut branch 2 times, most recently from 8e4f43f to b1a45f3 Compare November 28, 2025 13:49
@Karlson2k
Copy link
Contributor

Karlson2k commented Nov 28, 2025

Besides mentioned sub-optimal processing, the current code actually improves utmp file processing. The utmp API is not thread-safe (not even multi-process safe) by its nature.
The new version copies values from the file as soon as they are reported, significantly reducing chance of getting garbage by using late copy (and file modification by another process in parallel).

This should work better.

Any plans for a new release?

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Nov 28, 2025

Besides mentioned sub-optimal processing, the current code actually improves utmp file processing. The files is not thread-safe (not even multi-process safe) by its natures. The new version copies values from the file as soon as they are reported, significantly reducing chance of getting garbage by using late copy (and file modification by another process in parallel).

This should works better.

Any plans for a new release?

Yup, we release around the solstices. The plans for the next release are here:

This simplifies the implementation, allowing us to use _Generic(3) to
replace _Static_assert(3), is_same_type(), and local variables.

Local variables in macros can cause issues when nesting such macros.

This also makes the code more readable, by having the type explicitly
stated at call site.

Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Have xxx_T() macros that only add type safety to the function they wrap.

Have XXX() macros that do more magic; in this case, they deduce the
comparison function that is appropriate for the type.

This will allow using the xxx_T() macros in more complex cases, where
the function can't be deduced just from the type.

Signed-off-by: Alejandro Colomar <[email protected]>
This macro is like typeof(), but requires that the input is a type name.

This macro is useful because it parenthesizes the type name, which
allows one to create pointers to arbitrary types.  The following code
works for simple types:

	T  x;
	T  *p;

For example, that would work fine for 'int':

	int  x;
	int  *p;

However, that wouldn't work for types such as 'int[3]':

	int[3]  x;
	int[3]  *p;

But if we do instead

	typeas(T)  x;
	typeas(T)  *p;

now we can use 'int[3]' just fine:

	typeof((int[3]){})  x;
	typeof((int[3]){})  *p;

To understand this, we create a compound literal of type int[3], with
default values (zero, zero, zero), then get it's type, which is
obviously int[3].  And then we use that to declare a variable x of that
type, and a pointer p, which has type 'int(*)[3]'.

This would also work with something simpler.  One could use typeof(T)
directly:

	typeof(T)  x;
	typeof(T)  *p;

However, typeof() doesn't require that the input is a type; it accepts
arbitrary input.  The following is valid C:

	typeof(42)  x;

For our macro MALLOC(), where we want the second argument to be a type
name, we want to require that the argument is a type name.  For that, we
need to use typeas().

Reported-by: Alejandro Colomar <[email protected]>
Suggested-by: Thiago Adams <[email protected]>
Suggested-by: Martin Uecker <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Casts are unsafe.

Compound literals also have the ability of converting values, but they
don't have the unwanted effects on safety --casts disable most useful
diagnostics--.

Compound literals are lvalues, which means their address can be taken,
and they can also be assigned to.  To avoid this, we force lvalue
conversion through a statement expression.

Signed-off-by: Alejandro Colomar <[email protected]>
This should be more readable.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <[email protected]>
We don't use this value.  This silences a diagnostic about the unused
return value.

Signed-off-by: Alejandro Colomar <[email protected]>
This function already returned NULL on some errors.  It didn't make any
sense to exit(3) on allocation failure.  Instead, just return NULL.

Signed-off-by: Alejandro Colomar <[email protected]>
This aligns similar code, enhancing readability of the algorithm.

Signed-off-by: Alejandro Colomar <[email protected]>
This tells in a more readable way what kind of match we have.
I'll use this enum for simplifying other stuff in the following commit.

Signed-off-by: Alejandro Colomar <[email protected]>
As Evgeny reported, malloc(3) is overkill here.  He suggested using
other methods, which are less than ideal, but it's true that it's
overkill, and with the stack we can get something readable and more
efficient.

Reported-by: Evgeny Grin (Karlson2k) <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
This removes the reuse of 'ut', with which I wasn't entirely happy.
It also reduces the scope between setutxent()/endutxent().

Signed-off-by: Alejandro Colomar <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants