#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/sysmacros.h>
#include <stdio.h>
#include <time.h>
#include <strings.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <uuid/uuid.h>
#include <unistd.h>
#include <utime.h>
#include <ncurses.h>
#include <malloc.h>
#include <signal.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <atasmart.h>
#include <libfdisk/libfdisk.h>
#include <net/if.h>
#include <net/route.h>
#include <netdb.h>
#include <errno.h>
#include <ifaddrs.h>
#include <lzma.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_packet.h>
#include <linux/if_arp.h>
#include <linux/major.h>
#include <linux/types.h>
#include <linux/raid/md_u.h>
#include <net/ethernet.h>
#include <sys/sysmacros.h>
#include "gix.h"

void _debug_logger(char *__file__,int __line__,const char *fmt, ...);
#define debug_logger(fmt, ...) _debug_logger(__FILE__,__LINE__,fmt, ##__VA_ARGS__)


int is_zero(unsigned char *field,int len){
	int i;
	for ( i = 0; i < len; i++ ) 
		if ( field[i] ) return(0);
	return(1);
}


void _debug_logger(char *__file__,int __line__,const char *fmt, ...){
        char p[102400];
        va_list ap;
        struct timeval tv;
        struct tm _tm;
        struct tm *tm;
        char datestamp[512];
        FILE *log;
        gettimeofday(&tv,NULL);
        tm = localtime_r(&tv.tv_sec,&_tm);
        sprintf(datestamp,"[%04d-%02d-%02d %02d:%02d:%02d.%06d](%s:%d) (%d)",
                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
                tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec,
                __file__,__line__,
                getpid());

        va_start(ap,fmt);
        vsnprintf(p,102400,fmt,ap);
        va_end(ap);
        if ( DEBUG_LOGFILE ){
                log = fopen(DEBUG_LOGFILE,"a");
                if ( log ){
                        fprintf(log,"%s %s",datestamp,p);
                        fclose(log);
                }
        }
	// fprintf(stderr,"%s %s",datestamp,p);

}

#define SIZE_FACTOR 1024


char *showsize(u_int64_t size){
        static char dummy[64];
        double siz;
        siz = (double)size;

        if ( siz > ((u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR) ){
                sprintf(dummy,"%.02f TiBytes",siz / ((double)((u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR * (u_int64_t)SIZE_FACTOR)));
        } else if ( siz > (u_int64_t)(SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR) ){
                sprintf(dummy,"%.02f GiBytes",siz / ( SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR));
        } else if ( siz > (u_int64_t)(SIZE_FACTOR * SIZE_FACTOR) ){
                sprintf(dummy,"%.02f MiBytes",siz / (  SIZE_FACTOR * SIZE_FACTOR));
        } else if ( siz > (u_int64_t)(SIZE_FACTOR) ){
                sprintf(dummy,"%.02f KiBytes",siz / (SIZE_FACTOR));
        } else {
                sprintf(dummy,"%d Bytes",siz);
        }
        return(dummy);
}

char *generate_guid(char *newguid){
	uuid_t guid;

	uuid_generate(guid);
	uuid_unparse(guid,newguid);
	return(newguid);
}




/*
struct _part {
        char devname[16];
};


struct _raid {
        char devname[16];
        int level;
        struct _part  *part;
        int parts;
        u_int64_t size; // In Bytes
};
*/





#define getdevname(a,b) _getdevname("/dev",(a),(b))

char *_getdevname(char *_dir, int major, int minor){
	DIR *dir;
	static char fname[1024];
	struct dirent *diry;
	char *ptr;

	dir = opendir(_dir);
	if ( dir ){
		while ( (diry = readdir(dir)) ){
			struct stat st;
			if ( strcmp(diry->d_name,".") == 0 ) continue;
			if ( strcmp(diry->d_name,"..") == 0 ) continue;
			sprintf(fname,"%s/%s",_dir,diry->d_name);
			if ( stat(fname,&st) == 0 ){
				if ( S_ISBLK(st.st_mode)  ){
					if ( minor(st.st_rdev) == minor && major(st.st_rdev) == major ){
						ptr = fname;
						/*
						debug_logger("FOUND (%d:%d) --> [%s]\n",
							major,minor, ptr);
						*/
						closedir(dir);
						return(ptr);
					}
				}
			}
			
		}
		closedir(dir);
	}
	return(NULL);
}

struct _raid *gix_raidlist = NULL;
int gix_raidlists = 0;

#define rds gix_raidlists
#define rd gix_raidlist

struct _raid *checkraidmember(char *devname){
	int i,j;
	if ( rd ==  NULL ){
		update_raidlist();
	}
	if ( rd == NULL ) return(NULL);
	for ( i = 0; i < rds; i++ ){
		for ( j = 0; j < rd[i].parts; j++ ){
			if ( strcmp(devname,rd[i].part[j].devname) == 0 ) return(&rd[i]);
		}	
	}
	return(NULL);

}
struct _raid *getraid(char *devname){
	int i;
	if ( rd ==  NULL ){
		update_raidlist();
	}
	if ( rd == NULL ) return(NULL);
	for ( i = 0; i < rds; i++ ){
		if ( strcmp(rd[i].devname,devname) == 0 ) return(&rd[i]);
	}
	return(NULL);

}




void update_raidlist(){
	struct mdu_array_info_s array_info;
	char fname[1024];
	DIR *dir;
	struct dirent *diry;
	int i,j;
	if ( rds ){
		for ( i = 0; i < rds; i++ ){
			if ( rd[i].part ) free( rd[i].part );
		}
	}
	// debug_logger("Updating RAIDLIST\n");
	if ( rd ) free(rd);
	rd = NULL;
	rds = 0;
	dir = opendir("/dev");
	if ( dir ){
		struct stat st;
		while ( (diry = readdir(dir)) ){
			int npart;
			int fd;
			int fd2;
			u_int64_t smallest_size;
			if ( strlen(diry->d_name) <  3 ) continue;
			if ( memcmp(diry->d_name,"md",2) != 0 ) continue;
			sprintf(fname,"/dev/%s",diry->d_name);
			if ( stat(fname,&st) ) continue;
			fd = open(fname,O_RDONLY);
			if ( fd < 0 ) continue;
			if ( ioctl(fd,GET_ARRAY_INFO,&array_info) < 0 ){ close(fd); continue; } 
			smallest_size = 0;
			rd = (struct _raid *)realloc(rd,sizeof(struct _raid) * (rds+1));
			bzero(&rd[rds],sizeof(struct _raid));
			strcpy(rd[rds].devname,"/dev/");
			strcat(rd[rds].devname,diry->d_name);
			rd[rds].level = array_info.level;
			rd[rds].parts = array_info.raid_disks;
			rd[rds].part  = (struct _part *)malloc(sizeof(struct _part) * rd[rds].parts);
			npart = 0;
			for ( j = 0; j < 128; j++ ){
				mdu_disk_info_t dev_info;
				bzero(&dev_info,sizeof(dev_info));
				dev_info.number = j;
				if ( ioctl(fd,GET_DISK_INFO,&dev_info) == 0 ){
					char *ptr;
					ptr = getdevname(dev_info.major, dev_info.minor);
					if ( ptr == NULL ) continue;
					// debug_logger("Device name for %d, %d --> [%s] (%d/%d)\n",dev_info.major, dev_info.minor, ptr == NULL ? "NULL" : ptr,j,rd[rds].parts);

					strcpy(rd[rds].part[npart++].devname,ptr == NULL ? "-" : ptr);
					if ( ptr ){
						fd2 = open(ptr,O_RDONLY);
						if ( fd2 ){
							u_int64_t now_size;
							now_size = lseek(fd2,0,SEEK_END);
							if ( now_size < smallest_size || smallest_size == 0 ) smallest_size = now_size;
							close(fd2);
						}	
					}	
					if ( npart == rd[rds].parts ) break;
				} else {
					strcpy(rd[rds].part[j].devname,"--");
				}
			}
			close(fd);
		
			if ( rd[rds].level == 0 ){
				rd[rds].size = smallest_size * rd[rds].parts;
			} else if ( rd[rds].level == 1 ){
				rd[rds].size = smallest_size * (rd[rds].parts / 2);
			} else if ( rd[rds].level == 5 ){
				rd[rds].size = smallest_size * (rd[rds].parts - 1);
			} else if ( rd[rds].level == 6 ){
				rd[rds].size = smallest_size * (rd[rds].parts - 2);
			} else if ( rd[rds].level == 10 ){
				rd[rds].size = smallest_size * (rd[rds].parts / 2);
			} else {
				rd[rds].size = 0;	
			}
			rds++;	
			
			
		}
		closedir(dir);
	}
	/*
	debug_logger("Found %d Raid-Arrays\n",rds);
	for ( j = 0; j < rds; j++ ){
		debug_logger("Array [%s] contains %d disks, Total Size: %s (LEVEL: %d)\n",
			rd[j].devname,
			rd[j].parts,
			showsize(rd[j].size),
			rd[j].level);
		for ( i = 0; i < rd[j].parts; i++ ){
			debug_logger("--> [%s]\n",rd[j].part[i].devname);
		}
	}
	*/
}

#undef rd
#undef rds


int gix_get_prefixlen_ipv4(unsigned char *mask){
        int i,j;
        int prefixlen;

        prefixlen = 0;  
        for ( i = 0; i < 4; i++ ){
                for ( j = 0; j < 8; j++ ){
                        if ( (mask[i] >> j) & 0x01 ) prefixlen++;
                }
        }
        return(prefixlen);
}

int gix_get_prefixlen_ipv6(unsigned char *mask){
        int i,j;
        int prefixlen;

        prefixlen = 0;  
        for ( i = 0; i < 16; i++ ){
                for ( j = 0; j < 8; j++ ){
                        if ( (mask[i] >> j) & 0x01 ) prefixlen++;
                }
        }
        return(prefixlen);
}

unsigned char gix_bit_values_inverted[] = { 128,64,32,16,8,4,2,1 };



char *display_ipv4(unsigned char *addr, int addr_len, char *output){
        int i;
        *output = 0x00;
        for ( i = 0; i < addr_len; i++ ){
                sprintf(&output[strlen(output)],"%d",addr[i]);
                if ( i != ( addr_len - 1 ) ) strcat(output,".");
        }
        return(output);
}
char *display_ipv6(unsigned char *addr, int addr_len, char *output){
        int i;

	inet_ntop(AF_INET6,addr,output,1024);
	return(output);

        *output = 0x00;
        for ( i = 0; i < addr_len; i++ ){
                if ( i && (i % 2)  == 0 ) strcat(output,":");
                sprintf(&output[strlen(output)],"%02x",addr[i]);
        }
        return(output);
}


unsigned char *gix_apply_netmask_ipv6(unsigned char *netmask, unsigned char *ipv6, unsigned char *result){
        int i;
        for ( i = 0; i < 16; i++ ){
                result[i] = ipv6[i] & netmask[i];
        }
        return(result);
}
unsigned char *gix_apply_netmask_ipv4(unsigned char *netmask, unsigned char *ipv6, unsigned char *result){
        int i;
        for ( i = 0; i < 4; i++ ){
                result[i] = ipv6[i] & netmask[i];
        }
        return(result);
}

unsigned char  *gix_genmask_ipv6(int prefixlen, unsigned char *netmask){
        int i;
        memset(netmask,0,16);
        if ( prefixlen <= 0 ) return(netmask);
        for ( i = 0; i < 128; i++ ){
                if ( i < prefixlen ){
                        netmask[i>>3] |= gix_bit_values_inverted[i % 8];
                }                       
        }
        return(netmask);
}


unsigned char *gix_genmask_ipv4(int prefix_len, unsigned char *result){
        int i;
        uint32_t mask;
        mask = 0x00000000;
        for ( i = 1; i <= prefix_len && i < 33; i++ ){
                mask |= (1<<(32-i));
        }
        result[0] = (unsigned char) ((mask>>24) & 0xFF);
        result[1] = (unsigned char) ((mask>>16) & 0xFF);
        result[2] = (unsigned char) ((mask>>8) & 0xFF);
        result[3] = (unsigned char) (mask & 0xFF);
        return(result);
}

unsigned char *gix_gen_broadcast_ipv4(unsigned char *ipv4, int prefix_len, unsigned char *result){
	int i;
	unsigned char netmask[4];
	gix_genmask_ipv4(prefix_len,netmask);
	for ( i = 0; i < 4; i++ ){
		netmask[i] = ~netmask[i];
		result[i]  = netmask[i] | ipv4[i];
	}
	return(result);

}





char *get_tag(char *filename, char *tag){
	FILE *fp;
	static char buf[1024];
	char wanted[64];

	sprintf(wanted,"%s=",tag);
	if ( (fp = fopen(filename,"r")) == NULL ) return(NULL);
	while ( fgets(buf,1024,fp) ){
		if ( strncmp(buf,wanted,strlen(wanted)) == 0 ){
			fclose(fp);
			while(strlen(buf) && (buf[strlen(buf)-1] == '\r' || buf[strlen(buf)-1] == '\n' || isspace(buf[strlen(buf)-1]) ) ) buf[strlen(buf)-1] = 0x00;
			return(&buf[strlen(wanted)]);
		}
	}
	fclose(fp);
	return(NULL);
}



struct _interface *getinterface(char *sysconfpath, char *ifname){
	unsigned char zero[16];
	char dummy[1024];
	struct stat st;
	char config_file[1024];
	static struct _interface iffy;
	bzero(&iffy,sizeof(struct _interface));
	strcpy( iffy.name,ifname);
	sprintf(config_file,"%s/ifconfig.%s",sysconfpath,ifname);
	bzero(zero,16);
	// printf("SYSCONFPATH: [%s], Interface: [%s] --> [%s]\n",sysconfpath,ifname,config_file);
	if ( stat(config_file,&st) ){
		int sock;
		sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
		if ( sock > 0 ){
			unsigned char ip[4];
			char *value;
			in_addr_t ipli;
			struct ifreq ifr;	
			bzero(&ifr,sizeof(struct ifreq));
			strcpy(ifr.ifr_name,ifname);
			if ( ioctl(sock,SIOCGIFADDR,&ifr) == 0 ){
				ip[3] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 24) & 0xFF;
				ip[2] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 16) & 0xFF;
				ip[1] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 8) & 0xFF;
				ip[0] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 0) & 0xFF;
				memcpy(iffy.ip4,ip,4);
			}
			if ( ioctl(sock,SIOCGIFBRDADDR,&ifr) == 0 ){
				ip[3] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 24) & 0xFF;
				ip[2] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 16) & 0xFF;
				ip[1] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 8) & 0xFF;
				ip[0] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 0) & 0xFF;
				memcpy(iffy.broadcast_ip4,ip,4);
			}
			if ( ioctl(sock,SIOCGIFNETMASK,&ifr) == 0 ){
				ip[3] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 24) & 0xFF;
				ip[2] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 16) & 0xFF;
				ip[1] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 8) & 0xFF;
				ip[0] = ((((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr) >> 0) & 0xFF;
				iffy.prefixlen_ip4 = gix_get_prefixlen_ipv4(ip);
			}
			close(sock);	
		}	
		{
			FILE *fp;
			fp = fopen("/proc/net/if_inet6","r");
			if ( fp ){
				char buf[1024];
				while ( fgets(buf,1024,fp) ){
					char *ip;
					char *index;
					char *masklen;
					char *add1;
					char *add2;
					char *iface;
					char *hlp;
					hlp = buf;
					ip = strtok_r(buf," \n\r\t",&hlp);
					if ( ip == NULL ) continue;
					index = strtok_r(NULL," \n\r\t",&hlp);
					if ( index == NULL ) continue;
					masklen = strtok_r(NULL," \n\r\t",&hlp);
					if ( masklen == NULL ) continue;
					add1 = strtok_r(NULL," \n\r\t",&hlp);
					if ( add1 == NULL ) continue;
					add2 = strtok_r(NULL," \n\r\t",&hlp);
					if ( add2 == NULL ) continue;
					iface = strtok_r(NULL," \n\r\t",&hlp);
					if ( iface == NULL ) continue;
					if ( strcmp(iface,ifname) != 0 ) continue;
					sscanf(ip,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
							&iffy.ip6[0], &iffy.ip6[1], &iffy.ip6[2], &iffy.ip6[3],
							&iffy.ip6[4], &iffy.ip6[5], &iffy.ip6[6], &iffy.ip6[7],
							&iffy.ip6[8+0], &iffy.ip6[8+1], &iffy.ip6[8+2], &iffy.ip6[8+3],
							&iffy.ip6[8+4], &iffy.ip6[8+5], &iffy.ip6[8+6], &iffy.ip6[8+7]);
					sscanf(masklen,"%02x",&iffy.prefixlen_ip6);
				}
				fclose(fp);
			}
		}


	} else { 
		// Try to read life configuration
		unsigned char ip[4];
		char *value;
		in_addr_t ipli;
		
		value = get_tag(config_file,"ONBOOT");
		if ( value && ( value[0] == 'y' || value[0] == 'Y' ) ) iffy.active = 1;
		value = get_tag(config_file,"SERVICE");
		if ( value ) strcpy(iffy.service,value);

		value = get_tag(config_file,"PREFIX");
		if ( value ) iffy.prefixlen_ip4 = atoi(value);


		value = get_tag(config_file,"PREFIX6");
		if ( value ) iffy.prefixlen_ip6 = atoi(value);
		value = get_tag(config_file,"IP6");
		if ( value ) inet_pton(AF_INET6,value,iffy.ip6);
		value = get_tag(config_file,"GATEWAY6");
		if ( value ) inet_pton(AF_INET6,value,iffy.gw_ip6);

		value = get_tag(config_file,"IP");
		bzero(ip,4);
		if ( value ){
			ipli = inet_addr(value);	
			ip[3] = (ipli >> 24) & 0xFF;
			ip[2] = (ipli >> 16) & 0xFF;
			ip[1] = (ipli >> 8) & 0xFF;
			ip[0] = (ipli >> 0) & 0xFF;
		} 
		memcpy(iffy.ip4,ip,4);
		value = get_tag(config_file,"GATEWAY");
		bzero(ip,4);
		if ( value ){
			ipli = inet_addr(value);	
			ip[3] = (ipli >> 24) & 0xFF;
			ip[2] = (ipli >> 16) & 0xFF;
			ip[1] = (ipli >> 8) & 0xFF;
			ip[0] = (ipli >> 0) & 0xFF;
		} 
		memcpy(iffy.gw_ip4,ip,4);
		value = get_tag(config_file,"BROADCAST");
		bzero(ip,4);
		if ( value ){
			ipli = inet_addr(value);	
			ip[3] = (ipli >> 24) & 0xFF;
			ip[2] = (ipli >> 16) & 0xFF;
			ip[1] = (ipli >> 8) & 0xFF;
			ip[0] = (ipli >> 0) & 0xFF;
		} 
		memcpy(iffy.broadcast_ip4,ip,4);
	}
	if ( !is_zero(iffy.ip4,4) == 0 ) iffy.active = 1;
	if ( strlen(iffy.service) == 0 ) strcpy(iffy.service,"ipv46-static");
	

	return(&iffy);
}


int write_interface(char *sysconfigpath,struct _interface *iff){
	unsigned char fname[1024];
	FILE *fp;

	sprintf(fname,"%s/ifconfig.%s",sysconfigpath,iff->name);
	debug_logger("Writing interface [%s] to [%s]\n",iff->name,fname);
	unlink(fname);
	fp = fopen(fname,"w");
	if ( fp == NULL ) return(-1);
	fprintf(fp,"IFACE=%s\n",iff->name);
	fprintf(fp,"ONBOOT=%s\n",iff->active ? "yes" : "no");
	fprintf(fp,"SERVICE=ipv46-static\n");
	if ( !is_zero(iff->ip4,4) ){
		fprintf(fp,"IP=%s\n",display_ipv4(iff->ip4,4,fname));
		fprintf(fp,"PREFIX=%d\n",iff->prefixlen_ip4);
	}
	fprintf(fp,"BROADCAST=%s\n",display_ipv4(iff->broadcast_ip4,4,fname));
	if ( !is_zero(iff->gw_ip4,4) ){
		fprintf(fp,"GATEWAY=%s\n",display_ipv4(iff->gw_ip4,4,fname));
	}
	if ( !is_zero(iff->ip6,16 ) ) {
		char helper[1024];
		inet_ntop(AF_INET6,iff->ip6,helper,1024);
		fprintf(fp,"IP6=%s\n",helper);
		if ( !is_zero(iff->gw_ip6,16) ){
			inet_ntop(AF_INET6,iff->gw_ip6,helper,1024);
			fprintf(fp,"GATEWAY6=%s\n",helper);
		}
		fprintf(fp,"PREFIX6=%d\n",iff->prefixlen_ip6);
	}
	fclose(fp);

	return(0);
}


char *efi_show_attrs(u_int32_t val){
	static char result[1024];
	strcpy(result,"");
	if ( val & EFI_VARIABLE_NON_VOLATILE )      strcat(result,"NON_VOLATILE ");
	if ( val & EFI_VARIABLE_BOOTSERVICE_ACCESS ) strcat(result,"BOOTSERVICE_ACCESS ");
	if ( val & EFI_VARIABLE_RUNTIME_ACCESS ) strcat(result,"RUNTIME_ACCESS ");
	if ( val & EFI_VARIABLE_HARDWARE_ERROR_RECORD ) strcat(result,"HARDWARE_ERROR_RECORD ");
	if ( val & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS ) strcat(result,"AUTHENTICATED_WRITE_ACCESS ");
	if ( val & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE ) strcat(result,"TIME_BASED_AUTHENTICATED_WRITE ");
	if ( val & EFI_VARIABLE_APPEND_WRITE ) strcat(result,"APPEND_WRITE ");
	if ( val & EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS ) strcat(result,"ENHANCED_AUTHENTICATED_ACCESS ");
	return(result);
}


char *gix_hexdump(unsigned char *data, int data_len, char *rett){
	char *ret;
	int i;

	ret = rett;
	for ( i = 0; i < data_len; i++ ){
		if ( i ){ *ret = ','; ret++; }
		sprintf(ret,"%02X",data[i]);
		ret++;
		ret++;
	}	 
	*ret = 0x00;
	return(rett);

}

void dump_efi(struct _efivar *ef){
	char hexdump2[10240];
	int i;
	/*
	debug_logger("------------------------------------------------------------------------\n");
	debug_logger("Variable: [%s] (%d Bytes)\n",ef->name,ef->size);
	// debug_logger("Attrs: 0x%08X: [%s]\n",ef->attributes,efi_show_attrs(ef->attributes));
	debug_logger("Description: [%ls] (%d Bytes) (%s)\n",ef->description,ef->description_len,gix_hexdump((unsigned char *)ef->description,ef->description_len * 4,hexdump2));
	*/
	debug_logger("------------------------------------------------------------------------\n");
	if ( ef->bootorders ){
	}
	debug_logger("Found %d Bootorders (FILEFLAGS: [0x%04X], ACTIVE: [%c] (%04X) {%ls} [%s]\n",ef->bootorders,ef->fileflags,ef->active ? '*' : ' ' ,ef->entrynr,ef->description, ef->name);
	/*
	for ( i = 0; i < ef->bootorders; i++ ){
		debug_logger("BOOT-ORDER[%d]:  0x%04X\n",i,ef->bootorder[i]);
	}
	*/
	if ( ef->mdp ){
		wchar_t dat[1024];
		decode_utf16((uint16_t *)ef->mdp->file_path,dat,1024);
		debug_logger("MDP (Type 4): FILE_PATH: [%ls]\n",dat);
		debug_logger("Partition:       %d\n",ef->mdp->partnumber);
		debug_logger("Starting LBA:    %ju\n",ef->mdp->starting_lba);
		debug_logger("Partition Size:  %ju\n",ef->mdp->partition_size);
		debug_logger("MBR-Type:        %d\n",ef->mdp->mbr_type[0]);
		debug_logger("Signature-Type:  %d\n",ef->mdp->signature_type[0]);
		debug_logger("Signature (hex): [%s]\n",gix_hexdump(ef->mdp->signature,16,hexdump2));
	}
	for ( i = 0; i < ef->filepaths; i++ ){
		debug_logger("  FP[%d] TYPE: x%02X, Subtype: x%02X, LEN: %d Remain:[%d] DATA: [%s]\n",i,
				ef->filepath[i].type,
				ef->filepath[i].subtype,
				ef->filepath[i].length,
				ef->remain,
				ef->entrynr == 0xFFFF ? "-" : gix_hexdump(ef->filepath[i].data,ef->filepath[i].length,hexdump2));

	}
	if ( ef->remain && ef->remain_data ){
		wchar_t dat[1024];
		decode_utf16((uint16_t *)ef->remain_data,dat,1024);
		debug_logger("REMAIN: [%ls] DUMMY: 0x%02X, 0x%02X\n",dat,
				ef->dummy[0],ef->dummy[1]);
	}
	debug_logger("------------------------------------------------------------------------\n");
}


int create_efi_boot_entry(struct _efidisk *ef,unsigned char *bootlabel,unsigned char *bootimage,unsigned char *bootargs,u_int16_t *bootorder, int bootorders, int bootsel){
	char newuid[128];
	u_int16_t newentry;
	u_int16_t first_free;
	int i,j;
	char fname[1024];
	unsigned char buf[10240];
	int offset;
	int len_offset;
	int start_offset;
	u_int16_t offset_size;
	u_int64_t *h64;
	int fd;
	int retval;
	u_int16_t args_len;
	int result;
	char cmd[1024];
	char **arg;
	int args;


	arg = NULL;
	args = 0;

	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("/usr/sbin/efibootmgr");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-c");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-d");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = ef->device;
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-p");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	sprintf(cmd,"%d",ef->partno);
	arg[args++] = strdup(cmd);
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-L");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup(bootlabel);
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-l");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup(bootimage);
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup("-u");
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = strdup(bootargs);
	arg = (char **)realloc(arg,sizeof(char *) * ( args+1));
	arg[args++] = NULL;

	for ( i = 0; arg[i] != NULL; i++ ){
		debug_logger("EFIBOOTMGR ARG[%d] {%s}\n",i,arg[i]);

	}

	result = run_program(arg[0],arg,"Creating new Boot-Entry",NULL);
	if ( result != 0 ){
		return(-1);
	}
	return(0);


	bzero(newuid,128);
	for ( i = 0; i < 65535; i++ ){
		for ( j = 0; j < bootorders; j++ )
			if ( bootorder[j] == i ) break;
		if ( j == bootorders ) break; // We found a free one...
	}
	newentry = (u_int16_t)j;
	bootorders++;
	bootorder = (u_int16_t *) realloc(bootorder,bootorders * sizeof(u_int16_t));
	for ( i = bootorders - 1; i > bootsel; i-- ){
		bootorder[i] = bootorder[i-1];
	}
	bootorder[bootsel] = newentry;
	get_boot_guid(newuid);
	debug_logger("Creating new bootentry at entry: %d, Position: %d, UID: [%s]\n",newentry,bootsel,newuid);


	sprintf(fname,"%s/Boot%04X-%s",EFI_PATH,newentry,newuid);
	fd = open(fname,O_WRONLY | O_TRUNC | O_CREAT, 0644);
	if ( fd < 0 ){
		debug_logger("Cannot create [%s] (%s)\n",fname,strerror(errno));
		return(-1);
	}
	offset = 0;
	buf[offset++] = 0x07;
	buf[offset++] = 0x00;
	buf[offset++] = 0x00;
	buf[offset++] = 0x00;

	buf[offset++] = 0x01;
	buf[offset++] = 0x00;

	buf[offset++] = 0x00; // Reserved
	buf[offset++] = 0x00;

	args_len = strlen(bootargs) * 2;
	buf[offset++] = (unsigned char )((args_len >> 0) & 0xFF);
	buf[offset++] = (unsigned char )((args_len >> 8) & 0xFF);

	debug_logger("ARGSLEN: %d, BYTE[0]: [%02X] BYTE[1]: [%02X]\n",args_len,buf[offset-2],buf[offset-1]);


	for ( i = 0; i < strlen(bootlabel); i++ ){
		buf[offset++] = bootlabel[i];
		buf[offset++] = 0x00;
	}
	buf[offset++] = 0x00; // Label-End
	buf[offset++] = 0x00;

	// Create the File-Path-Entries
	//
	start_offset = offset;
	buf[offset++] = 0x04;
	buf[offset++] = 0x01;
	len_offset = offset;
	buf[offset++] = 0x00; // Length to be replaced later
	buf[offset++] = 0x00; // Length to be replaced later
			      
	buf[offset++] = (unsigned char) ef->partno;
	buf[offset++] = 0x00;

	buf[offset++] = 0x00; // Padding 2 Bytes
	buf[offset++] = 0x00;

	h64 = (u_int64_t *)&buf[offset];	
	*h64 = ef->start;
	offset += 8;
		
	h64 = (u_int64_t *)&buf[offset];	
	*h64 = ef->size;
	offset += 8;

	memcpy(&buf[offset],ef->serial,16);
	offset += 16;
	buf[offset++] = 0x02;  // We work with UUID
	buf[offset++] = 0x02;  // We work with gpt
	offset_size = (u_int16_t)(offset - start_offset);
	buf[len_offset] = (u_int8_t) ((offset_size >> 0) & 0xFF);
	buf[len_offset+1] = (u_int8_t) ((offset_size >> 8) & 0xFF);

	start_offset = offset;
	buf[offset++] = 0x04;
	buf[offset++] = 0x04;
	len_offset = offset;
	buf[offset++] = 0x00; // Length to be replaced later
	buf[offset++] = 0x00; // Length to be replaced later
	
	for ( i = 0; i < strlen(bootimage); i++ ){
		buf[offset++] = bootimage[i];
		buf[offset++] = 0x00;
	}
	buf[offset++] = 0x00; // Bootimage ended
	buf[offset++] = 0x00;

	offset_size = (u_int16_t)(offset - start_offset);
	buf[len_offset] = (u_int8_t) ((offset_size >> 0) & 0xFF);
	buf[len_offset+1] = (u_int8_t) ((offset_size >> 8) & 0xFF);

	buf[offset++] = 0x7f; // Filepath ended
	buf[offset++] = 0xff;
	buf[offset++] = 0x04;
	buf[offset++] = 0x00;

	// Now the remain starts...
	//
	for ( i = 0; i < strlen(bootargs); i++ ){
		buf[offset++] = bootargs[i];
		buf[offset++] = 0x00;
	}
	if ( (retval = write(fd,buf,offset)) != offset ){
		debug_logger("Failed to write %d/%d to [%s] (%s)\n",retval,offset,fname,strerror(errno));
		close(fd);
		unlink(fname);
		return(-2);
	}	
	close(fd);	

	debug_logger("Written %d Bytes to [%s]\n",offset,fname);
	efi_write_boot_order(bootorder,bootorders);


	return(0);

}


void encode_utf16(const wchar_t *input) {
    while (*input) {
        wchar_t c = *input++;
        if (c < 0x10000) {
            // BMP character
            printf("%04X ", c);
        } else {
            // Supplementary character
            c -= 0x10000;
            wchar_t high_surrogate = (c >> 10) + 0xD800;
            wchar_t low_surrogate = (c & 0x3FF) + 0xDC00;
            printf("%04X %04X ", high_surrogate, low_surrogate);
        }
    }
    printf("\n");
}

size_t decode_utf16(const uint16_t *input, wchar_t *output, size_t out_size) {
    size_t out_index = 0;
    while (*input && out_index < out_size - 1) {
        uint16_t first = *input++;
        if (first >= 0xD800 && first <= 0xDBFF) {
            // High surrogate
            uint16_t second = *input++;
            if (second >= 0xDC00 && second <= 0xDFFF) {
                // Valid surrogate pair
                wchar_t decoded = 0x10000 + ((first - 0xD800) << 10) + (second - 0xDC00);
                output[out_index++] = decoded;
            } else {
                // Invalid surrogate pair, handle error
                // fprintf(stderr, "Error: Invalid surrogate pair.\n");
                return 0;
            }
        } else {
            // BMP character
            output[out_index++] = first;
        }
    }
    output[out_index] = L'\0'; // Null-terminate the output string
    return out_index;
}

int efisorter(const void *_a, const void *_b){
	struct _efivar *a;
	struct _efivar *b;
	a = (struct _efivar *)_a;
	b = (struct _efivar *)_b;

	return((a->boot_order - b->boot_order));
}

#define efi (*_efi)
int load_efi_vars(struct _efivar **_efi){
	DIR *dir;
	struct dirent *diry;
	int efis;

	efi = NULL;
	efis = 0;


	dir = opendir(EFI_PATH);
	if ( dir ){
		while ( (diry = readdir(dir)) != 0 ){
			u_int32_t attributes;
			u_int16_t fileflags;
			u_int16_t desc_raw[1024];
			int desc_raw_len;
			unsigned char dummy[1024];
			size_t newpos;
			int fd;
			char fname[1024];
			struct stat st;
			int paths;
			struct _filepath fp;
			int the_skipper;
			int offset;
			offset = 0;
			if ( strcmp(diry->d_name,".") == 0 ) continue;
			if ( strcmp(diry->d_name,"..") == 0 ) continue;
			if ( strncmp(diry->d_name,"Boot",4) != 0 ) continue;
			sprintf(fname,"%s/%s",EFI_PATH,diry->d_name);
			if ( stat(fname,&st) ) continue;
			if ( !S_ISREG(st.st_mode) ) continue;
			if ((fd = open(fname,O_RDONLY)) < 0 ) continue;
			if ( read(fd,&attributes,4) != 4 ){ close(fd); continue; }
			offset += 4;
			if ( !(attributes & EFI_VARIABLE_NON_VOLATILE) ) continue;
			if ( !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS) ) continue;
			if ( !(attributes & EFI_VARIABLE_RUNTIME_ACCESS) ) continue;
			if ( read(fd,&fileflags,2) != 2 ){ close(fd); continue; } // Filepathlist-Len, but sometimes junk, so we find out by parsing
			offset += 2;
			// debug_logger("THE_SIZE:  [%02X] [%02X]\n",dummy[0], dummy[1]);									    
			// the_skipper = (int) dummy[1] + 1;
			if ( read(fd,dummy,2) != 2 ){ close(fd); continue; }
			if ( read(fd,dummy,2) != 2 ){ close(fd); continue; }


			
			efi = (struct _efivar *)realloc(efi,sizeof(struct _efivar) * (efis+1));
			bzero(&efi[efis],sizeof(struct _efivar));
			efi[efis].attributes = attributes;
			efi[efis].dummy[0] = dummy[0];
			efi[efis].dummy[1] = dummy[1];
			strcpy(efi[efis].name,diry->d_name);
			if ( sscanf(diry->d_name,"Boot%04x-",&efi[efis].entrynr) != 1 ){
				efi[efis].entrynr = 0xFFFF;
			}
			efi[efis].size = st.st_size;
			efi[efis].fileflags = fileflags;
			if ( (efi[efis].fileflags & 0xFF) == 0x09 || (efi[efis].fileflags & 0xFF) == 0x01 ){
				efi[efis].active = 1;
			} else {
				efi[efis].active = 0;
			}

			desc_raw_len = 0;
			while ( read(fd,&desc_raw[desc_raw_len],2) == 2 ){ // It is 16Bit Unicode...but normaly we do not use Umlaute...
				offset += 2;
				if ( desc_raw[desc_raw_len++] == 0 ) break;
			}
			efi[efis].description_len = decode_utf16(desc_raw,efi[efis].description,1024);


			// OK...we parse the file-path-list now...
			efi[efis].filepath  = NULL;
			efi[efis].filepaths = 0;
			while ( read(fd,&fp,4) == 4 ){
				offset += 4; 
				fp.length -= 4;
				if ( fp.length  ){
					fp.data = (unsigned char *)malloc(fp.length);
					offset += fp.length;
					if ( read(fd,fp.data,fp.length) != fp.length ) fp.length *= -1;
				} else {
					fp.data = NULL;
				}
				if ( fp.type == 0x04 ){
					struct _mdp mdp;
					if ( efi[efis].mdp ){
						memcpy(&mdp,efi[efis].mdp,sizeof(struct _mdp));
					} else {
						bzero(&mdp,sizeof(struct _mdp));
					}
					if ( fp.subtype == 0x04 ){
						memcpy(mdp.file_path,fp.data,fp.length);
					}
					if ( fp.subtype == 0x01 ){
						char hexdump2[1024];
						int the_offset; 
						the_offset = 0;
						mdp.partnumber = fp.data[the_offset]; the_offset += 2;
						the_offset += 2; // Skip padding...
						mdp.starting_lba  =  (*((u_int64_t *)&fp.data[the_offset])); the_offset += 8;
						mdp.partition_size = (*((u_int64_t *)&fp.data[the_offset])); the_offset += 8;
						memcpy(mdp.signature,&fp.data[the_offset],16); the_offset += 16;
						mdp.mbr_type[0] = fp.data[the_offset++];
						mdp.signature_type[0] = fp.data[the_offset++];
					}
					efi[efis].mdp = (struct _mdp *)malloc(sizeof(struct _mdp));
					memcpy(efi[efis].mdp,&mdp,sizeof(struct _mdp));
				}
				efi[efis].filepath = (struct _filepath *)realloc(efi[efis].filepath,sizeof(struct _filepath) * (efi[efis].filepaths + 1));
				memcpy(&efi[efis].filepath[efi[efis].filepaths],&fp,sizeof(struct _filepath));
				if ( efi[efis].filepath[efi[efis].filepaths].type == 127 ) break;
				efi[efis].filepaths++;
			}
			efi[efis].remain = st.st_size - offset;
			if ( efi[efis].remain > 0 ){
				efi[efis].remain_data = (unsigned char *)malloc(efi[efis].remain + 2);
				bzero(efi[efis].remain_data, efi[efis].remain + 2);
				read(fd,efi[efis].remain_data,efi[efis].remain);
			} else {
				efi[efis].remain_data = NULL;
			}
			close(fd);
			if ( strncmp(diry->d_name,"BootOrder",9) == 0 ){ 
				u_int16_t boot;
				int ii;
				fd = open(fname,O_RDONLY);
				read(fd,dummy,4); // Read attributes 
				efi[efis].bootorders = 0;
				efi[efis].bootorder = NULL;
				while ( read(fd,&boot,2) == 2 ){
					efi[efis].bootorder = (u_int16_t *)realloc(efi[efis].bootorder,sizeof(u_int16_t) * (efi[efis].bootorders + 1));
					efi[efis].bootorder[efi[efis].bootorders] = boot;
					efi[efis].bootorders++;
				}
				close(fd);
				if ( efis ){
					for ( ii = 0; ii < efis; ii++ ){
						efi[ii].bootorder = efi[efis].bootorder;
						efi[ii].bootorders = efi[efis].bootorders;
					}
				}
			}
			if ( efi[0].bootorder && efis ){
				efi[efis].bootorder = efi[0].bootorder;
				efi[efis].bootorders = efi[0].bootorders;
			}
			efis++;
		}
	}	
	{
		int i,j;
		for ( i = 0; i < efis; i++ ){
			int j; 
			if ( efi[i].entrynr == 0xFFFF ) continue;
			for ( j = 0; j < efi->bootorders; j++ ){
				if ( efi->bootorder[j] == efi[i].entrynr ){ efi[i].boot_order = j; break; }
			}

	}
	}
	qsort(efi,efis,sizeof(struct _efivar),efisorter);
	return(efis);
}
#undef efi


void get_boot_guid(char *bootguid){
	DIR *dir;
	struct dirent *diry;
	dir = opendir(EFI_PATH);
	while ( (diry = readdir(dir)) != NULL ){
		if ( strncmp(diry->d_name,"BootOrder-",10) == 0 ) { 
			strcpy(bootguid,&diry->d_name[10]);
			break;
		}
	}
	closedir(dir);
	return;
}

void efi_write_boot_order_file(char *fname, u_int16_t *bootorder, int bootorders){
	int fd,i;
	unsigned char what_to_write[8192];
	int written;
	int offset;
	offset = 0;
	what_to_write[offset++] = 0x07;
	what_to_write[offset++] = 0x00;
	what_to_write[offset++] = 0x00;
	what_to_write[offset++] = 0x00;
	for ( i = 0; i < bootorders; i++ ){
		what_to_write[offset++] = (unsigned char)((bootorder[i] >> 0) & 0xFF);
		what_to_write[offset++] = (unsigned char)((bootorder[i] >> 8) & 0xFF);
	}
	debug_logger("Writing Boot-order (%d entries) to [%s] (%d Bytes, %d entries)\n",bootorders,fname,offset,bootorders);
	fd = open(fname,O_WRONLY | O_TRUNC);
	if ( fd < 0 ){
		debug_logger("Open for writing of [%s] failed [%s]\n",fname,strerror(errno));
		return;
	}
	if ( (written = write(fd,what_to_write,offset)) != offset ){
		debug_logger("Failed to write (%d/%d) to [%s]\n",written,offset,strerror(errno));
		return;
	}
	close(fd);
	return;
}

void efi_write_boot_order(u_int16_t *bootorder, int bootorders){
	DIR *dir;
	struct dirent *diry;
	dir = opendir(EFI_PATH);
	if ( dir == NULL ) return;
	while ( (diry = readdir(dir)) != NULL ){
		if ( strncmp(diry->d_name,"BootOrder-",10) == 0 ) { 
			char fname[1024];
			sprintf(fname,"%s/%s",EFI_PATH,diry->d_name);
			efi_write_boot_order_file(fname,bootorder, bootorders);
			break;
		}
	}
	closedir(dir);
	return;
}




void uuid_to_serial(char *uuid, unsigned char *serial){
	unsigned char hlp[3];
	unsigned int digit;
	int offset;
	offset = 0;

	bzero(serial,16);

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[3] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[2] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[1] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[0] = (unsigned char)digit;
	offset += 3;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[5] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[4] = (unsigned char)digit;
	offset += 3;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[7] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[6] = (unsigned char)digit;
	offset += 3;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[8] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[9] = (unsigned char)digit;
	offset += 3;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[10] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[11] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[12] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[13] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[14] = (unsigned char)digit;
	offset += 2;

	strncpy(hlp,&uuid[offset],2);
	sscanf(hlp,"%02x",&digit);
	serial[15] = (unsigned char)digit;
	offset += 2;


}

u_int64_t swap_bytes(u_int64_t value) {
    return ((value & 0x00000000000000FFULL) << 56) |
           ((value & 0x000000000000FF00ULL) << 40) |
           ((value & 0x0000000000FF0000ULL) << 24) |
           ((value & 0x00000000FF000000ULL) << 8) |
           ((value & 0x000000FF00000000ULL) >> 8) |
           ((value & 0x0000FF0000000000ULL) >> 24) |
           ((value & 0x00FF000000000000ULL) >> 40) |
           ((value & 0xFF00000000000000ULL) >> 56);
}
