/* GNU Guix --- Functional package management for GNU
   Copyright (C) 2022 Ricardo Wurmus <rekado@elephly.net>

   This file is part of GNU Guix.

   GNU Guix is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or (at
   your option) any later version.

   GNU Guix 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.  */

/*

  This is stupid, but so is almost anything in the periphery of computers.
  This code compiles to an executable that can be prepended to any binary;
  when executed it sets environment variables that have been set at compile
  time and then re-executes itself without the prepended binary.

  Example to build a wrapped "yes" binary named "yes.wrapped":
  
  thing=$(readlink -f $(type -p yes)); \
    gcc -std=gnu99 -fPIC -Os -DENVVARS='"hello","1","world","bleh"' \
      -DORIGINAL_SIZE=$(stat --format=%s $thing) -Wall \
      /path/to/elf-wrap.c -o elf-wrap && \
    cat elf-wrap $thing > $(basename $thing).wrapped && \
    chmod --reference=$thing $(basename $thing).wrapped
 */

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <unistd.h>

extern char **environ;

#define ARR(...) {__VA_ARGS__, NULL}

int
main (int argc, char *argv[])
{
  int in;
  int out;
  if ((in = open (argv[0], O_RDONLY, 0)) < 0)
    {
      fprintf (stderr, "cannot read self as file");
      exit(EXIT_FAILURE);
    }
  
  /* write binary without wrapper to in-memory file */
  out = memfd_create ("guix-wrapper", 0);
  if (out == -1)
    {
      fprintf (stderr, "cannot create in-memory fd");
      exit(EXIT_FAILURE);
    }

  struct stat stat_buf;
  fstat (in, &stat_buf);
  off_t offset = stat_buf.st_size - ORIGINAL_SIZE;
  sendfile (out, in, &offset, ORIGINAL_SIZE);
  close (in);
  fcntl (out, FD_CLOEXEC);

  /* set env vars and run the wrapped binary */
  char *env[] = ARR(ENVVARS);
  unsigned int i = 0;
  for (i = 0; env[i] != NULL; i+=2)
    {
      setenv (env[i], env[i+1], 1);
    }
  
  fexecve(out, argv, environ);

  return EXIT_FAILURE;
}

Generated by Ricardo Wurmus using scpaste at Fri Jul 15 23:52:07 2022. CEST. (original)