/* 
 * execl-subuidgid.c
 * 
 * Copyright (c) 2018-2019 Eric Vidal <eric@obarun.org>
 * 
 * All rights reserved.
 * 
 * This file is part of Obarun. It is subject to the license terms in
 * the LICENSE file found in the top-level directory of this
 * distribution.
 * This file may not be copied, modified, propagated, or distributed
 * except according to the terms contained in the LICENSE file./
 */
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <pwd.h>

#include <oblibs/error2.h>
#include <oblibs/environ.h>

#include <skalibs/types.h>
#include <skalibs/buffer.h>
#include <skalibs/stralloc.h>
#include <skalibs/strerr2.h>
#include <skalibs/env.h>
#include <skalibs/sgetopt.h>
#include <skalibs/djbunix.h>

#include <execline/execline.h>

#define USAGE "execl-subuidgid [ -h ] [ -o owner ] prog..."

static inline void info_help (void)
{
  static char const *help =
"execl-subuidgid <options> prog\n"
"\n"
"options :\n"
"	-h: print this help\n" 
"	-o: owner to use\n"
;

 if (buffer_putsflush(buffer_1, help) < 0)
    strerr_diefu1sys(111, "write to stdout") ;
}

/** Implement again this function coming from
 * 66. This is avoid the dependency from it*/
static int youruid(uid_t *passto,char const *owner)
{
	int e ;
	e = errno ;
	errno = 0 ;
	struct passwd *st ;
	if (!(st = getpwnam(owner)) || errno)
	{
		if (!errno) errno = EINVAL ;
		return 0 ;
	}
	*passto = st->pw_uid ;
	errno = e ;
	return 1 ;
}

static int yourgid(gid_t *passto,uid_t owner)
{
	int e ;
	e = errno ;
	errno = 0 ;
	struct passwd *st ;
	if (!(st = getpwuid(owner)) || errno)
	{
		if (!errno) errno = EINVAL ;
		return 0 ;
	}
	*passto = st->pw_gid ;
	errno = e ;
	return 1 ;
}

int main (int argc, char const **argv, char const *const *envp)
{
	uid_t uid ;
	gid_t gid ;
	int r ;
	char const *owner = 0 ;
	stralloc sa = STRALLOC_ZERO ;
	stralloc dst = STRALLOC_ZERO ;
	exlsn_t info = EXLSN_ZERO;
	char cuid[UID_FMT], cgid[GID_FMT] ;
	
	PROG = "execl-subuidgid" ;
	
	{
		subgetopt_t l = SUBGETOPT_ZERO ;
		for (;;)
		{
		  int opt = subgetopt_r(argc, argv, "ho:", &l) ;
		  if (opt == -1) break ;
		  switch (opt)
		  {
			case 'h' : info_help(); return 0 ;
			case 'o' : owner = l.arg ; break ;
			default : exitusage(USAGE) ;
		  }
		}
		argc -= l.ind ; argv += l.ind ;
	}
	if (owner)
	{
		if (!youruid(&uid,owner)) strerr_diefu2sys(111,"get uid of: ",owner) ;
	}
	else uid = getuid() ;
		
	if (!yourgid(&gid,uid)) strerr_diefu1sys(111,"get gid") ;
	cuid[uid_fmt(cuid,uid)] = 0 ;
	cgid[gid_fmt(cgid,gid)] = 0 ;
	
	if (!environ_add_key_val("UID",cuid,&info)) strerr_diefu1sys(111,"set UID") ;
	if (!environ_add_key_val("GID",cgid,&info)) strerr_diefu1sys(111,"set GID") ;
	
	if (!env_string(&sa,argv,(unsigned int) argc)) strerr_diefu1sys(111,"environment string") ;
	
	r = el_substitute (&dst, sa.s, sa.len, info.vars.s, info.values.s,
		genalloc_s (elsubst_t const, &info.data),genalloc_len (elsubst_t const, &info.data)) ;
	if (r < 0) strerr_diefu1sys(111,"el_substitute") ;
	else if (!r) _exit(0) ;
	
	stralloc_free(&sa) ;
	
	{
        char const *v[r + 1];
        if (!env_make (v, r, dst.s, dst.len)) strerr_diefu1sys (111, "env_make") ;
        v[r] = 0 ;
        pathexec_r (v, envp, env_len (envp), info.modifs.s, info.modifs.len) ;
    }

	return 0 ;	
}