Home | History | Annotate | Download | only in disktest
      1 /*
      2 * Disktest
      3 * Copyright (c) International Business Machines Corp., 2001
      4 *
      5 *
      6 * This program is free software; you can redistribute it and/or modify
      7 * it under the terms of the GNU General Public License as published by
      8 * the Free Software Foundation; either version 2 of the License, or
      9 * (at your option) any later version.
     10 *
     11 * This program is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 * GNU General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU General Public License
     17 * along with this program; if not, write to the Free Software
     18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     19 *
     20 *  Please send e-mail to yardleyb (at) us.ibm.com if you have
     21 *  questions or comments.
     22 *
     23 *  Project Website:  TBD
     24 *
     25 * $Id: sfunc.c,v 1.8 2009/02/26 12:02:23 subrata_modak Exp $
     26 *
     27 */
     28 #include <sys/types.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <stdarg.h>
     32 #include <signal.h>
     33 #ifdef WINDOWS
     34 #include <winsock2.h>
     35 #include <process.h>
     36 #include <windows.h>
     37 #include <winbase.h>
     38 #include <winioctl.h>
     39 #else
     40 #ifdef AIX
     41 #include <sys/ioctl.h>
     42 #include <sys/devinfo.h>
     43 #endif
     44 #include <unistd.h>
     45 #include <ctype.h>
     46 #endif
     47 
     48 #include <time.h>
     49 #include <errno.h>
     50 #include <fcntl.h>
     51 #include <string.h>
     52 #ifdef LINUX
     53 #include <endian.h>
     54 #endif
     55 
     56 #include "main.h"
     57 #include "sfunc.h"
     58 #include "defs.h"
     59 #include "globals.h"
     60 #include "io.h"
     61 #include "threading.h"
     62 
     63 /*
     64  * Generates a random 32bit number.
     65  */
     66 long Rand32(void)
     67 {
     68 	/*
     69 	 * based on the fact that rand returns
     70 	 * 0 - 0x7FFF
     71 	 */
     72 	long myRandomNumber = 0;
     73 
     74 	myRandomNumber = ((long)(rand() & 0x7FFF)) << 16;
     75 	myRandomNumber |= ((long)(rand() & 0x7FFF)) << 1;
     76 	myRandomNumber |= ((long)(rand() & 0x1));
     77 
     78 	return (myRandomNumber);
     79 }
     80 
     81 /*
     82  * Generates a random 64bit number.
     83  */
     84 OFF_T Rand64(void)
     85 {
     86 	OFF_T myRandomNumber = 0;
     87 
     88 	myRandomNumber = ((OFF_T) (rand() & 0x7FFF)) << 48;
     89 	myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 33;
     90 	myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 18;
     91 	myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 3;
     92 	myRandomNumber |= ((OFF_T) (rand() & 0x7));
     93 
     94 	return (myRandomNumber);
     95 }
     96 
     97 /*
     98 * could not find a function that represented a conversion
     99 * between a long long and a string.
    100 */
    101 OFF_T my_strtofft(const char *pStr)
    102 {
    103 	OFF_T value = 0;
    104 	int bOct = 0, bHex = 0;
    105 
    106 	int neg = 0;
    107 
    108 	for (;; pStr++) {
    109 		switch (*pStr) {
    110 		case '0':
    111 			bOct = 1;
    112 			continue;
    113 		case 'x':
    114 			if (bOct)
    115 				bHex = 1;
    116 			continue;
    117 		case ' ':
    118 		case '\t':
    119 			continue;
    120 		case '-':
    121 			neg = 1;
    122 		 /*FALLTHROUGH*/ case '+':
    123 			pStr++;
    124 		}
    125 		break;
    126 	}
    127 	if ((!bOct) && (!bHex)) {
    128 		while (*pStr >= '0' && *pStr <= '9') {
    129 			value = (value * 10) + (*pStr++ - '0');
    130 		}
    131 	} else if (bHex) {
    132 		while ((*pStr >= '0' && *pStr <= '9') ||
    133 		       (*pStr >= 'A' && *pStr <= 'F') ||
    134 		       (*pStr >= 'a' && *pStr <= 'f')) {
    135 			if (*pStr >= '0' && *pStr <= '9')
    136 				value = (value << 4) + (*pStr++ - '0');
    137 			else if (*pStr >= 'A' && *pStr <= 'F')
    138 				value = (value << 4) + 10 + (*pStr++ - 'A');
    139 			else if (*pStr >= 'a' && *pStr <= 'f')
    140 				value = (value << 4) + 10 + (*pStr++ - 'a');
    141 		}
    142 	} else if (bOct) {
    143 		while (*pStr >= '0' && *pStr <= '7') {
    144 			value = (value * 8) + (*pStr++ - '0');
    145 		}
    146 	}
    147 	return (neg ? -value : value);
    148 }
    149 
    150 /*
    151 * prints messages to stdout. with added formating
    152 */
    153 int pMsg(lvl_t level, const child_args_t * args, char *Msg, ...)
    154 {
    155 #define FORMAT "| %s | %s | %d | %s | %s | %s"
    156 #define TIME_FORMAT "%04d/%02d/%02d-%02d:%02d:%02d"
    157 #define TIME_FMT_LEN 20
    158 	va_list l;
    159 	int rv = 0;
    160 	size_t len = 0;
    161 	char *cpTheMsg;
    162 	char levelStr[10];
    163 	struct tm struct_time;
    164 	struct tm *pstruct_time;
    165 	char time_str[TIME_FMT_LEN];
    166 	time_t my_time;
    167 
    168 	extern unsigned long glb_flags;
    169 
    170 #ifndef WINDOWS
    171 	static pthread_mutex_t mTime = PTHREAD_MUTEX_INITIALIZER;
    172 #endif
    173 
    174 #ifndef WINDOWS
    175 	LOCK(mTime);
    176 #endif
    177 
    178 	time(&my_time);
    179 	pstruct_time = localtime(&my_time);
    180 	if (pstruct_time != NULL)
    181 		memcpy(&struct_time, pstruct_time, sizeof(struct tm));
    182 	else
    183 		memset(&struct_time, 0, sizeof(struct tm));
    184 #ifndef WINDOWS
    185 	UNLOCK(mTime);
    186 #endif
    187 
    188 	if ((glb_flags & GLB_FLG_QUIET) && (level == INFO))
    189 		return 0;
    190 
    191 	va_start(l, Msg);
    192 
    193 	if (glb_flags & GLB_FLG_SUPRESS) {
    194 		rv = vprintf(Msg, l);
    195 		va_end(l);
    196 		return rv;
    197 	}
    198 
    199 	switch (level) {
    200 	case START:
    201 		strcpy(levelStr, "START");
    202 		break;
    203 	case END:
    204 		strcpy(levelStr, "END  ");
    205 		break;
    206 	case STAT:
    207 		strcpy(levelStr, "STAT ");
    208 		break;
    209 	case INFO:
    210 		strcpy(levelStr, "INFO ");
    211 		break;
    212 	case DBUG:
    213 		strcpy(levelStr, "DEBUG");
    214 		break;
    215 	case WARN:
    216 		strcpy(levelStr, "WARN ");
    217 		break;
    218 	case ERR:
    219 		strcpy(levelStr, "ERROR");
    220 		break;
    221 	}
    222 
    223 	sprintf(time_str, TIME_FORMAT, struct_time.tm_year + 1900,
    224 		struct_time.tm_mon + 1,
    225 		struct_time.tm_mday,
    226 		struct_time.tm_hour, struct_time.tm_min, struct_time.tm_sec);
    227 
    228 	len += strlen(FORMAT);
    229 	len += strlen(time_str);
    230 	len += strlen(levelStr);
    231 	len += sizeof(pid_t) * 8 + 1;
    232 	len += strlen(VER_STR);
    233 	len += strlen(args->device);
    234 	len += strlen(Msg);
    235 
    236 	if ((cpTheMsg = (char *)ALLOC(len)) == NULL) {
    237 		printf
    238 		    ("Can't print formatted message, printing message raw.\n");
    239 		rv = vprintf(Msg, l);
    240 		va_end(l);
    241 		return rv;
    242 	}
    243 
    244 	memset(cpTheMsg, 0, len);
    245 	sprintf(cpTheMsg, FORMAT, time_str, levelStr, args->pid, VER_STR,
    246 		args->device, Msg);
    247 
    248 	rv = vprintf(cpTheMsg, l);
    249 	FREE(cpTheMsg);
    250 
    251 	va_end(l);
    252 	return rv;
    253 }
    254 
    255 OFF_T getByteOrderedData(const OFF_T data)
    256 {
    257 	OFF_T off_tpat = 0;
    258 
    259 #ifdef WINDOWS
    260 	unsigned char *ucharpattern;
    261 	size_t i = 0;
    262 
    263 	ucharpattern = (unsigned char *)&data;
    264 	for (i = 0; i < sizeof(OFF_T); i++) {
    265 		off_tpat |=
    266 		    (((OFF_T) (ucharpattern[i])) << sizeof(OFF_T) *
    267 		     ((sizeof(OFF_T) - 1) - i));
    268 	}
    269 #endif
    270 
    271 #ifdef AIX
    272 	off_tpat = data;
    273 #endif
    274 
    275 #ifdef LINUX
    276 #if __BYTE_ORDER == __LITTLE_ENDIAN
    277 	unsigned char *ucharpattern;
    278 	size_t i = 0;
    279 
    280 	ucharpattern = (unsigned char *)&data;
    281 	for (i = 0; i < sizeof(OFF_T); i++) {
    282 		off_tpat |=
    283 		    (((OFF_T) (ucharpattern[i])) << sizeof(OFF_T) *
    284 		     ((sizeof(OFF_T) - 1) - i));
    285 	}
    286 #else
    287 	off_tpat = data;
    288 #endif
    289 #endif
    290 
    291 	return off_tpat;
    292 }
    293 
    294 void mark_buffer(void *buf, const size_t buf_len, void *lba,
    295 		 const child_args_t * args, const test_env_t * env)
    296 {
    297 	OFF_T *plocal_lba = lba;
    298 	OFF_T local_lba = *plocal_lba;
    299 	OFF_T *off_tbuf = buf;
    300 	OFF_T off_tpat = 0, off_tpat2 = 0, off_tpat3 = 0, off_tpat4 = 0;
    301 	OFF_T pass_count = env->pass_count;
    302 	OFF_T start_time = (OFF_T) env->start_time;
    303 	unsigned char *ucharBuf = (unsigned char *)buf;
    304 	size_t i = 0;
    305 	extern char hostname[];
    306 
    307 	off_tpat2 = getByteOrderedData(pass_count);
    308 	if (args->flags & CLD_FLG_ALT_MARK) {
    309 		off_tpat3 = getByteOrderedData(args->alt_mark);
    310 	} else {
    311 		off_tpat3 = getByteOrderedData(start_time);
    312 	}
    313 	off_tpat4 = getByteOrderedData(args->seed);
    314 
    315 	for (i = 0; i < buf_len; i = i + BLK_SIZE) {
    316 		if (args->flags & CLD_FLG_MRK_LBA) {
    317 			/* fill first 8 bytes with lba number */
    318 			off_tpat = getByteOrderedData(local_lba);
    319 			*(off_tbuf + (i / sizeof(OFF_T))) = off_tpat;
    320 		}
    321 		if (args->flags & CLD_FLG_MRK_PASS) {
    322 			/* fill second 8 bytes with pass_count */
    323 			*(off_tbuf + (i / sizeof(OFF_T)) + 1) = off_tpat2;
    324 		}
    325 		if (args->flags & CLD_FLG_MRK_TIME) {
    326 			/* fill third 8 bytes with start_time */
    327 			*(off_tbuf + (i / sizeof(OFF_T)) + 2) = off_tpat3;
    328 		}
    329 		if (args->flags & CLD_FLG_MRK_SEED) {
    330 			/* fill fourth 8 bytes with seed data */
    331 			*(off_tbuf + (i / sizeof(OFF_T)) + 3) = off_tpat4;
    332 		}
    333 		if (args->flags & CLD_FLG_MRK_HOST) {
    334 			/* now add the hostname to the mark data */
    335 			memcpy(ucharBuf + 32 + i, hostname, HOSTNAME_SIZE);
    336 		}
    337 		if (args->flags & CLD_FLG_MRK_TARGET) {
    338 			/* now add the target to the mark data */
    339 			memcpy(ucharBuf + 32 + HOSTNAME_SIZE + i, args->device,
    340 			       strlen(args->device));
    341 		}
    342 
    343 		local_lba++;
    344 	}
    345 }
    346 
    347 /*
    348 * function fill_buffer
    349 * This function fills the passed buffer with data based on the pattern and patten type.
    350 * for pattern types of counting the pattern does not matter.  For lba pattern type, the
    351 * pattern will be the address of the lba.
    352 */
    353 
    354 void fill_buffer(void *buf, size_t len, void *pattern, size_t pattern_len,
    355 		 const unsigned int pattern_type)
    356 {
    357 	size_t i, j;
    358 	unsigned char *ucharbuf = buf;
    359 	OFF_T *off_tbuf = buf;
    360 	unsigned char *ucharpattern = pattern;
    361 	OFF_T *poff_tpattern = pattern;
    362 	OFF_T off_tpat, off_tpat2;
    363 
    364 	switch (pattern_type) {	/* the pattern type should only be one of the following */
    365 	case CLD_FLG_CPTYPE:
    366 		/* Will fill buffer with counting pattern 0x00 thru 0xff */
    367 		for (i = 0; i < len; i++)
    368 			ucharbuf[i] = (unsigned char)(i & 0xff);
    369 		break;
    370 	case CLD_FLG_FPTYPE:
    371 		/* arrange data to go on the wire correctly */
    372 		off_tpat = 0;
    373 		for (j = 0; j < (sizeof(OFF_T) / pattern_len); j++)
    374 			for (i = 0; i < pattern_len; ++i)
    375 #ifdef WINDOWS
    376 				off_tpat |=
    377 				    (((OFF_T) (ucharpattern[i])) << 8 *
    378 				     (7 - ((j * pattern_len) + i)));
    379 #endif
    380 #ifdef AIX
    381 		off_tpat |=
    382 		    (((OFF_T) (ucharpattern[(8 - pattern_len) + i])) << 8 *
    383 		     (7 - ((j * pattern_len) + i)));
    384 #endif
    385 #ifdef LINUX
    386 #if __BYTE_ORDER == __LITTLE_ENDIAN
    387 		off_tpat |=
    388 		    (((OFF_T) (ucharpattern[i])) << 8 *
    389 		     (7 - ((j * pattern_len) + i)));
    390 #else
    391 		off_tpat |=
    392 		    (((OFF_T) (ucharpattern[(8 - pattern_len) + i])) << 8 *
    393 		     (7 - ((j * pattern_len) + i)));
    394 #endif
    395 #endif
    396 
    397 		/* fill buffer with fixed pattern */
    398 		for (i = 0; i < len / 8; i++)
    399 			*(off_tbuf + i) = off_tpat;
    400 		break;
    401 	case CLD_FLG_LPTYPE:
    402 		off_tpat2 = *poff_tpattern;
    403 		for (j = 0; j < len; j++) {
    404 			/* arrange data to go on the wire correctly */
    405 			ucharpattern = (unsigned char *)&off_tpat2;
    406 			off_tpat = 0;
    407 			for (i = 0; i < pattern_len; i++)
    408 #ifdef WINDOWS
    409 				off_tpat |=
    410 				    (((OFF_T) (ucharpattern[i])) << 8 *
    411 				     (7 - i));
    412 #endif
    413 #ifdef AIX
    414 			off_tpat |=
    415 			    (((OFF_T) (ucharpattern[(8 - pattern_len) + i])) <<
    416 			     8 * (7 - i));
    417 #endif
    418 #ifdef LINUX
    419 #if __BYTE_ORDER == __LITTLE_ENDIAN
    420 			off_tpat |=
    421 			    (((OFF_T) (ucharpattern[i])) << 8 * (7 - i));
    422 #else
    423 			off_tpat |=
    424 			    (((OFF_T) (ucharpattern[(8 - pattern_len) + i])) <<
    425 			     8 * (7 - i));
    426 #endif
    427 #endif
    428 
    429 			/* fill buffer with lba number */
    430 			for (i = 0; i < BLK_SIZE / 8; i++) {
    431 				*(off_tbuf + i + (j * (BLK_SIZE / 8))) =
    432 				    off_tpat;
    433 			}
    434 			off_tpat2++;
    435 		}
    436 		break;
    437 	case CLD_FLG_RPTYPE:
    438 		/* Will fill buffer with a random pattern.
    439 		 * Unfortunatly, every LBA, 512 bytes of data will be
    440 		 * the same random data set, this is due to the LBA
    441 		 * boundary requirement of disktest.  This should be fixed
    442 		 * at some point...
    443 		 */
    444 		for (i = 0; i < BLK_SIZE / sizeof(OFF_T); i++)
    445 			*(off_tbuf + i) = Rand64();
    446 
    447 		for (i = BLK_SIZE; i < len; i += BLK_SIZE)
    448 			memcpy((ucharbuf + i), ucharbuf, BLK_SIZE);
    449 		break;
    450 	default:
    451 		printf("Unknown fill pattern\n");
    452 		exit(1);
    453 	}
    454 }
    455 
    456 void normalize_percs(child_args_t * args)
    457 {
    458 	int i, j;
    459 
    460 	if ((args->flags & CLD_FLG_R) && !(args->flags & CLD_FLG_W)) {
    461 		if ((args->flags & CLD_FLG_DUTY) && (args->rperc < 100)) {
    462 			pMsg(WARN, args,
    463 			     "Read specified w/o write, ignoring -D, forcing read only...\n");
    464 		}
    465 		args->rperc = 100;
    466 		args->wperc = 0;
    467 	} else if ((args->flags & CLD_FLG_W) && !(args->flags & CLD_FLG_R)) {
    468 		if ((args->flags & CLD_FLG_DUTY) && (args->wperc < 100)) {
    469 			pMsg(WARN, args,
    470 			     "Write specified w/o read, ignoring -D, forcing write only...\n");
    471 		}
    472 		args->rperc = 0;
    473 		args->wperc = 100;
    474 	} else {		/* must be reading and writing */
    475 		if (args->rperc == 0 && args->wperc == 0) {
    476 			args->rperc = 50;
    477 			args->wperc = 50;
    478 		} else if (args->rperc == 0) {
    479 			args->rperc = 100 - args->wperc;
    480 		} else if (args->wperc == 0) {
    481 			args->wperc = 100 - args->rperc;
    482 		}
    483 	}
    484 
    485 	if (args->rperc + args->wperc != 100) {
    486 		pMsg(INFO, args,
    487 		     "Balancing percentage between reads and writes\n");
    488 		if ((args->flags & CLD_FLG_R) && (args->flags & CLD_FLG_W)) {
    489 			i = 100 - (args->rperc + args->wperc);
    490 			j = i / 2;
    491 			args->wperc += j;
    492 			args->rperc += (i - j);
    493 		}
    494 	}
    495 }
    496 
    497 #ifndef WINDOWS
    498 char *strupr(char *String)
    499 {
    500 	unsigned int i;
    501 
    502 	for (i = 0; i < strlen(String); i++) {
    503 		*(String + i) = toupper(*(String + i));
    504 	}
    505 	return (String);
    506 }
    507 
    508 char *strlwr(char *String)
    509 {
    510 	unsigned int i;
    511 
    512 	for (i = 0; i < strlen(String); i++) {
    513 		*(String + i) = tolower(*(String + i));
    514 	}
    515 	return (String);
    516 }
    517 #endif
    518 
    519 OFF_T get_file_size(char *device)
    520 {
    521 	OFF_T size = 0;
    522 	fd_t fd;
    523 
    524 #ifdef WINDOWS
    525 	SetLastError(0);
    526 
    527 	fd = CreateFile(device,
    528 			GENERIC_READ,
    529 			FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    530 #else
    531 	fd = open(device, 0);
    532 #endif
    533 
    534 	if (INVALID_FD(fd)) {
    535 		return size;
    536 	}
    537 
    538 	size = SeekEnd(fd);
    539 	size /= BLK_SIZE;
    540 
    541 	CLOSE(fd);
    542 	return size;
    543 }
    544 
    545 OFF_T get_vsiz(const char *device)
    546 {
    547 #ifdef PPC
    548 	unsigned long size = 0;
    549 #else
    550 	OFF_T size = 0;
    551 #endif
    552 
    553 #ifdef WINDOWS
    554 	HANDLE hFileHandle;
    555 	BOOL bRV;
    556 	DWORD dwLength;
    557 	GET_LENGTH_INFORMATION myLengthInfo;
    558 	DISK_GEOMETRY DiskGeom;
    559 
    560 	hFileHandle = CreateFile(device,
    561 				 GENERIC_READ,
    562 				 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    563 
    564 	if (hFileHandle == INVALID_HANDLE_VALUE) {
    565 		return (GetLastError());
    566 	}
    567 
    568 	SetLastError(0);
    569 	bRV = DeviceIoControl(hFileHandle,
    570 			      IOCTL_DISK_GET_LENGTH_INFO,
    571 			      NULL,
    572 			      0,
    573 			      &myLengthInfo,
    574 			      sizeof(GET_LENGTH_INFORMATION), &dwLength, NULL);
    575 
    576 	if (bRV) {
    577 		size = myLengthInfo.Length.QuadPart;
    578 		size /= BLK_SIZE;	/* return requires BLOCK */
    579 	} else {
    580 		bRV = DeviceIoControl(hFileHandle,
    581 				      IOCTL_DISK_GET_DRIVE_GEOMETRY,
    582 				      NULL,
    583 				      0,
    584 				      &DiskGeom,
    585 				      sizeof(DISK_GEOMETRY), &dwLength, NULL);
    586 
    587 		if (bRV) {
    588 			size = (OFF_T) DiskGeom.Cylinders.QuadPart;
    589 			size *= (OFF_T) DiskGeom.TracksPerCylinder;
    590 			size *= (OFF_T) DiskGeom.SectorsPerTrack;
    591 		} else {
    592 			size = 0;
    593 		}
    594 	}
    595 	CloseHandle(hFileHandle);
    596 #else
    597 	int fd = 0;
    598 #if AIX
    599 	struct devinfo *my_devinfo = NULL;
    600 	unsigned long ulSizeTmp;
    601 #endif
    602 
    603 	if ((fd = open(device, 0)) < 0) {
    604 		return 0;
    605 	}
    606 #if AIX
    607 	my_devinfo = (struct devinfo *)ALLOC(sizeof(struct devinfo));
    608 	if (my_devinfo != NULL) {
    609 		memset(my_devinfo, 0, sizeof(struct devinfo));
    610 		if (ioctl(fd, IOCINFO, my_devinfo) == -1)
    611 			size = -1;
    612 		else {
    613 			if (my_devinfo->flags & DF_LGDSK) {
    614 				ulSizeTmp =
    615 				    (unsigned long)my_devinfo->un.scdk64.
    616 				    hi_numblks;
    617 				size |=
    618 				    ((((OFF_T) ulSizeTmp) << 32) &
    619 				     0xFFFFFFFF00000000ll);
    620 				ulSizeTmp =
    621 				    (unsigned long)my_devinfo->un.scdk64.
    622 				    lo_numblks;
    623 				size |=
    624 				    (((OFF_T) ulSizeTmp) &
    625 				     0x00000000FFFFFFFFll);
    626 			} else {
    627 				ulSizeTmp =
    628 				    (unsigned long)my_devinfo->un.scdk.numblks;
    629 				size |=
    630 				    (((OFF_T) ulSizeTmp) &
    631 				     0x00000000FFFFFFFFll);
    632 			}
    633 		}
    634 		FREE(my_devinfo);
    635 	}
    636 #else
    637 	if (ioctl(fd, BLKGETSIZE, &size) == -1)
    638 		size = -1;
    639 #endif
    640 
    641 	close(fd);
    642 #endif
    643 
    644 #ifdef PPC
    645 	return ((OFF_T) size);
    646 #else
    647 	return (size);
    648 #endif
    649 }
    650 
    651 #ifndef WINDOWS
    652 void Sleep(unsigned int msecs)
    653 {
    654 	usleep(msecs * 1000);
    655 }
    656 #endif
    657 
    658 fmt_time_t format_time(time_t seconds)
    659 {
    660 	fmt_time_t time_struct;
    661 
    662 	time_struct.days = seconds / 86400;
    663 	time_struct.hours = (seconds % 86400) / 3600;
    664 	time_struct.minutes = (seconds % 3600) / 60;
    665 	time_struct.seconds = seconds % 60;
    666 
    667 	return time_struct;
    668 }
    669