Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 10 Aug 2002 14:02:39 +0200
From:      Eirik Nygaard <eirikn@bluezone.no>
To:        current@freebsd.org
Subject:   Rewrite of the perl script rmuser to C
Message-ID:  <20020810120239.GB1064@test.eirikn.net>

next in thread | raw e-mail | index | archive | help

--Hf61M2y+wYpnELGG
Content-Type: multipart/mixed; boundary="ncSAzJYg3Aa9+CRW"
Content-Disposition: inline


--ncSAzJYg3Aa9+CRW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

I have just rewritten the rmuser perl script to C, it would be great if you=
 could take a look at it and check if everything is ok.

How do I get this commited?

--=20

Eirik Nygaard <eirikn@bluezone.no>
PGP Key: 83C55EDE


--ncSAzJYg3Aa9+CRW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rmuser.c"
Content-Transfer-Encoding: quoted-printable

/*
** -*- perl -*-
** Copyright 1995, 1996, 1997 Guy Helmer, Ames, Iowa 50014.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer as
**    the first lines of this file unmodified.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products=
      #    derived from this software without specific prior written permis=
sion.
**
** THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** rmuser - C programme to remove users
**
** Eirik Nygaard <eirikn@bluezone.no>, 08/08/02
**
** $FreeBSD$
*/

/*
** TODO:
** Add an at remove function
*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <dirent.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <sys/wait.h>


char passwd_file[] =3D "/etc/master.passwd";
char passwd_tmp[] =3D "/etc/ptmp";
char group_file[] =3D "/etc/group";
char new_group_file[] =3D "/etc/group.new";
char mail_dir[] =3D "/var/mail";
char crontab_dir[] =3D "/var/cron/tabs";
char path[] =3D "/bin:/sbin:/usr/bin:/usr/sbin";

int yes =3D 0; // Always yes?
int removehomedir =3D 1;
char *user =3D NULL; // User to delete
char user2[400];
char answer[400];
int fp;
FILE *fp2;
char line[1024];
char homedir[1024];

struct passwd *password;
struct stat sb;

void usage(char *progname);
void getuser();
void remove_files_from_dir(int uid, char *path);
int recvnl( char *buf, int fd);
void update_passwd();
void update_group();
void killuser(int uid, int gid);
void del_mail();
void sig_handler1();
void sig_handler2();
void sig_handler3();
void sig_handler4();

int main(int argc, char **argv) {
	int ch, numbuf =3D 0;
	char string[1024], string2[1024], *p;
	struct sigaction sa;

	/* Check for root */
	if (getuid() !=3D 0) {
		printf("You must be root to run this program.\n");
		_exit(-2);
	}
	sa.sa_handler =3D sig_handler1;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags =3D SA_RESTART;
	sigaction(SIGINT, &sa, NULL);
	sa.sa_handler =3D sig_handler2;
	sigaction(SIGQUIT, &sa, NULL);
	sa.sa_handler =3D sig_handler3;
	sigaction(SIGHUP, &sa, NULL);
	sa.sa_handler =3D sig_handler4;
	sigaction(SIGTERM, &sa, NULL);
=09
=09
	/* Set the path we need */
	setenv("PATH", path, 1);
=09
	/* Set umode */
	umask(022);
=09
	/* Get command line arguments */
	while ((ch =3D getopt(argc, argv, "yu:")) !=3D -1) {
		switch (ch) {
			case 'y':
				yes =3D 1;
				break;
			case 'u':
				user =3D optarg;
				break;
			case '?':
			default:
				usage(argv[0]);
		}
	}
=09
	if ((fp =3D open(passwd_file, O_RDONLY)) =3D=3D NULL) {
		printf("Unable to open passwd file(%s).\n", passwd_file);
		_exit(-3);
	}
	fcntl(fp, F_SETFD, 1);
	if (flock(fp, LOCK_EX|LOCK_NB) =3D=3D -1) {
		printf("Unable to lock passwd file(%s).\n", passwd_file);
		_exit(-4);
	}

=09
	if (user =3D=3D NULL) {
		getuser();
	}
=09
	while (recvnl(string, fp) !=3D -1) {
		strcpy(string2, string);
		if ((p =3D strtok(string, ":")) !=3D NULL) {
			if (strcasecmp(p, user) =3D=3D 0) {
				printf("Matching password entry: \n\n");
				printf("%s\n", string2);
				goto next1;
			}
		}
	}
	printf("No user by that name found.\n");
	_exit(-6);
	next1:
	if (yes =3D=3D 0) {
		printf("Is this the entry you wish to remove?(y/n) ");
		fgets(answer, sizeof(answer), stdin);
		if (strncmp(answer, "y", 1) !=3D 0 && strncmp(answer, "Y", 1) !=3D 0) {
			printf("User %s not removed.\n", user);
			_exit(1);
		}
	}
	if (flock(fp, LOCK_UN) =3D=3D -1) {=09
		printf("Unable to unlock passwd file(%s).\n", passwd_file);
		_exit(-5);
	}
	close(fp);
=09
	if ((password =3D getpwnam(user)) =3D=3D NULL) {
		printf("Unable to get user info for user %s.\n", user);
		_exit(-11);
	}
	if (yes =3D=3D 0) {
		printf("Remove homedir(%s)?(y/n) ", password->pw_dir);
		fgets(answer, sizeof(answer), stdin);
		if (strncmp(answer, "y", 1) =3D=3D 0 || strncmp(answer, "Y", 1) =3D=3D 0)=
=20
			removehomedir =3D 1;
		else=20
			removehomedir =3D 0;
	} else {
		removehomedir =3D 1;
	}
	strncpy(homedir, password->pw_dir, sizeof(homedir));
	next2:
	lstat(homedir, &sb);
	if (removehomedir =3D=3D 1) {
		if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode)) {
			printf("Home %s is not a directory, so it won't be removed\n", homedir);
			removehomedir =3D 0;
		}
		if (S_ISLNK(sb.st_mode)) {
			numbuf =3D readlink(password->pw_dir, homedir, sizeof(homedir));
			printf("%s\n", homedir);
			homedir[numbuf] =3D '\0';
			goto next2;
		}
		if (sb.st_uid !=3D password->pw_uid) {
			if (removehomedir =3D=3D 1)
				printf("Home %s is not owned by %s, so it will not be removed.\n", home=
dir, user);
			removehomedir =3D 0;
		}
	}
	killuser(password->pw_uid, password->pw_gid);
	update_passwd();
	update_group();
	if (removehomedir =3D=3D 1) {
		printf("Removeing home %s.\n", homedir);
		remove_files_from_dir(password->pw_uid, homedir);
		if (rmdir(homedir) =3D=3D -1)
			printf("Warning: Unable to remove home %s - continuing.\n", homedir);
	}
=09
	sprintf(line, "%s/%s", crontab_dir, user);
	if ((fp2 =3D fopen(line, "r")) !=3D NULL) {
		fclose(fp2);
		if (unlink(line) =3D=3D -1)
			printf("Warning: Unable to remove crontab file %s - continuing.\n", line=
);
		printf("Removeing users' crontab: ");
		sprintf(line, "/usr/bin/crontab -u %s -r", user);
		system(line);
		printf("done.\n");
	}
	del_mail();
	printf("Removing files belonging to %s from /tmp.\n", user);
	remove_files_from_dir(password->pw_uid, "/tmp");
	printf("Removing files belonging to %s from /var/tmp.\n", user);
	remove_files_from_dir(password->pw_uid, "/var/tmp");
=09
	return 0;
}

void usage(char *progname) {
	printf("Usage: %s [-y] [-u username]\n", progname);=09
	_exit(-1);
}
void getuser() {
	printf("Enter login name for user to remove: ");
	fgets(user2, sizeof(user2), stdin);
	user2[strlen(user2) - 1] =3D '\0';
	user =3D user2;
}

void remove_files_from_dir(int uid, char *path) {
	int Tmp;
	struct dirent *DirEntryPtr;
	DIR *DirPtr;
	struct stat Stat;
	char Path[1024];
=09
	DirPtr =3D opendir(path);
	while (1) {
		DirEntryPtr =3D readdir(DirPtr);=09
		if (DirEntryPtr =3D=3D 0) break;
		if (strcmp(DirEntryPtr->d_name,".") !=3D 0 && strcmp(DirEntryPtr->d_name,=
"..") !=3D 0) {
			Path[0] =3D 0;
			strcat(Path,homedir);
			strcat(Path,"/");
			strcat(Path,DirEntryPtr->d_name);
			Tmp =3D stat(Path,&Stat);=20
			if (S_ISDIR(Stat.st_mode)) {
				remove_files_from_dir(uid, Path);
				if (Stat.st_uid =3D=3D uid)
					if (rmdir(Path) =3D=3D -1)
				 		printf("Warning: Unable to remove dir %s - continuing.\n", Path);
			}
			if (Stat.st_uid =3D=3D uid) {
				//printf("Removeing file %s\n", Path);
				if (unlink(Path) =3D=3D -1)=20
					printf("Warning: unlink on %s failed - continuing.\n", Path);
			}
	=09
		}
	=09
	}
	return;
}

void update_passwd() {
	int fp_pw, fp2_pw;
	char string[1024], string2[1024];
	int skipped =3D 0;
=09
	if ((fp_pw =3D open(passwd_file, O_RDONLY|O_EXLOCK)) =3D=3D -1) {
		printf("Warning: Unable to open %s, so can not remove the user - continui=
ng.\n", passwd_file);
		return;
	}
	if ((fp2_pw =3D open(passwd_tmp, O_WRONLY|O_EXLOCK|O_CREAT, 0600)) =3D=3D =
-1) {
		printf("Warning: Unable to open %s, so can not remove the user - continui=
ng.\n", passwd_tmp);
		return;
	}
	sprintf(string2, "%s:", user);
	next3:
	while (recvnl(string, fp) !=3D -1) {
		if (strncmp(string, "#", 1) =3D=3D 0) goto next3;
		if (strncasecmp(string, string2, strlen(user) + 1) !=3D 0) {
			write(fp2_pw, string, strlen(string));
			write(fp2_pw, "\n", 1);
		}=20
		else {
			if (skipped =3D=3D 1) {
				write(fp2_pw, string, strlen(string));
				write(fp2_pw, "\n", 1);
			}
			//printf("Droped entry for %s\n", string);
			skipped =3D 1;
		}
	}
	if (skipped =3D=3D 0) {
		printf("Whoops! Didn't find %s's entry second time around!\n", user);
		close(fp_pw); close(fp2_pw);
		sprintf(line, "/bin/rm -f %s", passwd_tmp);
		system(line);
		_exit(-10);
	}
=09
	close(fp_pw); close(fp2_pw);
	/* Rebuild db */
	sprintf(line, "/usr/sbin/pwd_mkdb -p %s", passwd_tmp);
	system(line);
=09
	return;
}

void update_group() {
	char string[1024], string2[1024], string3[1024];
	int fp_g, fp2_g, users;
	char *p, *p2;
	int a =3D 0;
	char group[1024], gid[1024], pass[1024], users2[1024], users3[1024];

	if ((fp_g =3D open(group_file, O_RDONLY|O_EXLOCK)) =3D=3D -1) {
      printf("Warning: Unable to open %s, so can not remove the user's grou=
p - continuing.\n", passwd_file);
      return;
   } =20
   if ((fp2_g =3D open(new_group_file, O_WRONLY|O_EXLOCK|O_CREAT, 0644)) =
=3D=3D -1) {
      printf("Warning: Unable to open %s, so can not remove the user's grou=
p - continuing.\n", passwd_tmp);
      return;
   } =20
	sprintf(string2, "%s:", user);
	next4:
	while (recvnl(string, fp) !=3D -1) {
		if (strncmp(string, "#", 1) =3D=3D 0) goto next4;
		if (strncasecmp(string, string2, strlen(user) + 1) !=3D 0) {
			// Check if user is in the group
			string3[0] =3D '\0';
			strcpy(string3, string);
			for (p =3D strtok(string3, ":\n"); p !=3D NULL; p =3D strtok(NULL, ":\n"=
)) {
				if (a =3D=3D 0)
					strcpy(group, p);
				if (a =3D=3D 1)
					strcpy(pass, p);
				if (a =3D=3D 2)
					strcpy(gid, p);
				if (a =3D=3D 3)
					strcpy(users2, p);
				a++;

			}
			a =3D 0;
			users3[0] =3D '\0';
			for (p =3D strtok(users2, ",\n"); p !=3D NULL; p =3D strtok(NULL, ",\n")=
) {
				if (strcasecmp(user, p) !=3D 0) {
					if (a =3D=3D 0) {
						strcat(users3, p);
						a =3D 1;
					} else {=20
						strcat(users3, ",");
						strcat(users3, p);
					}
				}
			}
			sprintf(string3, "%s:%s:%s:%s\n", group, pass, gid, users3);
			/*printf("<%s>\n", string3);*/
		=09
	      write(fp2_g, string3, strlen(string3));
			a =3D 0;
	     }
	      else {
				// Check if there is other users added to the group
				p =3D string;
				while (*p !=3D ':') *p++; // Skip groupname
				*p++;
				while (*p !=3D ':') *p++; // Skip password sectiona
				*p++;
				while (*p !=3D ':') *p++; // Skip gid section
				*p++;
				strcpy(string3, p);
				for (p2 =3D strtok(string3, ","); p2 !=3D NULL; p2 =3D strtok(NULL, ","=
)) {
					users =3D 1;
				}
				if (users =3D=3D 1) {
					printf("Warning: Other users in group %s, not removing - continuing.\n=
", user);
					write(fp2_g, string, strlen(string));
				} else {
	         	//printf("Droped entry %s\n", string);
				}
	      }
	}
	close(fp_g); close(fp2_g);																																		=09
	rename(new_group_file, group_file);

	return;
}

int recvnl( char *buf, int fd) {
   int bytes =3D 0;
	char buf2[2];
	char buf3[1024];

   buf3[0] =3D '\0';
	while ( read(fd, buf2, 1) !=3D 0) {
		buf2[1] =3D '\0';
		if (strncmp(buf2, "\n", 1) =3D=3D 0) {
			strcpy(buf, buf3);
		   return bytes;
	   }
	   else if (strncmp(buf2, "\r", 1) !=3D 0) {
		   strcat(buf3, buf2);
	      bytes++;
	   }
   }

   return -1;
}

/* Kill users processes */
void killuser(int uid, int gid) {
	int pid;=09
=09
	if (( pid =3D fork()) =3D=3D 0) {
		setgid(gid);
		setuid(uid);
		if (getuid() !=3D uid || getgid() !=3D gid) {
			printf("Warning: Unable to set gid or uid, so I can't kill the users pro=
cesses - continuing.\n");
			_exit(-1);
		}
		kill(0, 15);
		sleep(1);
		kill(0, 9);
		_exit(0);
	} else {
		while (waitpid(-1,NULL,WNOHANG) !=3D pid)
			;
	}
}

void del_mail() {
	char string[1024];
	FILE *file;
=09
=09
	sprintf(string, "%s/%s", mail_dir, user);
	if ((file =3D fopen(string, "r")) !=3D NULL) {
		printf("Removing %s mail file.\n", user);
		fclose(file);
		if (unlink(string) =3D=3D -1)
			printf("\tWarning: Unable to remove mail file %s.\n", string);
	}
=09
	return;
}

void sig_handler1() {
	printf("\nCaught signal SIGINT -- cleaning up.\n");
	unlink(passwd_tmp);
	unlink(new_group_file);
	_exit(5);
}

void sig_handler2() {
   printf("\nCaught signal SIGQUIT -- cleaning up.\n");
   unlink(passwd_tmp);
   unlink(new_group_file);
	_exit(5);
}

void sig_handler3() {
   printf("\nCaught signal SIGHUP -- cleaning up.\n");
   unlink(passwd_tmp);
   unlink(new_group_file);
	_exit(5);
}

void sig_handler4() {
	if (getuid() !=3D 0)
		return;
   printf("\nCaught signal SIGTERM -- cleaning up.\n");
   unlink(passwd_tmp);
   unlink(new_group_file);
	_exit(5);
}
		=09

--ncSAzJYg3Aa9+CRW--

--Hf61M2y+wYpnELGG
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (FreeBSD)

iD8DBQE9VQDe1JB0Z4PFXt4RAl0xAJ9dTA1VDdkMCCEhUNRsX98AmMuS6ACfQ0Id
KF1u61Beyps7bYPDc887eak=
=Nt30
-----END PGP SIGNATURE-----

--Hf61M2y+wYpnELGG--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020810120239.GB1064>