#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <errno.h>
#include <sys/stat.h>

/* Valid chars in service name */
#define VALID_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_"
#define DEPLOY_AS_SH "/usr/local/bin/deploy_user.sh"

/* Translate username to uid/gid */
struct passwd * getUid(char *name) {
    struct passwd *p;
    if ((p = getpwnam(name)) == NULL) {
		printf("Error: User '%s' not found.\n", name);
		exit(1);
    }
    return p;
}


/* Run deploy as user */
int runServiceDeployment(char *service) {
  /* Set uid and gid */
  struct passwd *p = getUid(service);
  setgid(p->pw_gid);
  setuid(p->pw_uid);


  /* Check if executable exists */
  if ( access( DEPLOY_AS_SH, F_OK ) == -1 ) {
	  printf("No such file: %s\n", DEPLOY_AS_SH);
	  exit(1);
  }

  /* Stat executable */
  struct stat sb;
  if (stat(DEPLOY_AS_SH, &sb) != 0) {
	  printf("cant stat file: %s\n", DEPLOY_AS_SH);
	  exit(1);
  }

  /* Test if file is executable */
  if ( ! (sb.st_mode & S_IXUSR )) {
	  printf("Not executable: %s\n", DEPLOY_AS_SH);
	  exit(1);
  }

  /* Test if regular file */
  if (! S_ISREG(sb.st_mode)) {
	  printf("Not a regular file: %s\n", DEPLOY_AS_SH);
	  exit(1);
  }

  /* Execute DEPLOY_AS_SH */
  if (execl("/bin/bash", "--noediting", "--noprofile", "--norc", "--", DEPLOY_AS_SH, service, (char *)NULL) == -1) {
  	printf("Error exec %s\n", DEPLOY_AS_SH);
	printf("%s\n", strerror(errno));
	exit(1);
  }
  return 0;
}


/* Just check only allowed chars are present */
int validate (char *s) {
	for (int i=0; i<strlen(s); i++){
		int found = 0;
		for (int j=0; j<strlen(VALID_CHARS); j++) {
			if ( s[i] == VALID_CHARS[j] ) {
				found = 1;
				break;
			}
		}
		if (!found) {
			return 1;
		}
	}
	return 0;
}


int main(int argc, char *argv[] )
{
    if ( argc != 2) {
        printf("usage: %s <service-name>\n",argv[0]);
        exit(1);
    }

	if (validate(argv[1]) != 0) {
		printf("Bad service name\n");
		exit(1);
	}
    runServiceDeployment(argv[1]);
    return 0;
}


