xref: /onnv-gate/usr/src/cmd/filebench/common/misc.c (revision 5184:da60d2b4a9e2)
1*5184Sek110237 /*
2*5184Sek110237  * CDDL HEADER START
3*5184Sek110237  *
4*5184Sek110237  * The contents of this file are subject to the terms of the
5*5184Sek110237  * Common Development and Distribution License (the "License").
6*5184Sek110237  * You may not use this file except in compliance with the License.
7*5184Sek110237  *
8*5184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5184Sek110237  * or http://www.opensolaris.org/os/licensing.
10*5184Sek110237  * See the License for the specific language governing permissions
11*5184Sek110237  * and limitations under the License.
12*5184Sek110237  *
13*5184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
14*5184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
16*5184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
17*5184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5184Sek110237  *
19*5184Sek110237  * CDDL HEADER END
20*5184Sek110237  */
21*5184Sek110237 /*
22*5184Sek110237  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5184Sek110237  * Use is subject to license terms.
24*5184Sek110237  */
25*5184Sek110237 
26*5184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5184Sek110237 
28*5184Sek110237 #include <stdio.h>
29*5184Sek110237 #include <fcntl.h>
30*5184Sek110237 #include <limits.h>
31*5184Sek110237 #include <time.h>
32*5184Sek110237 #include <libgen.h>
33*5184Sek110237 #include <unistd.h>
34*5184Sek110237 #include <strings.h>
35*5184Sek110237 #include "filebench.h"
36*5184Sek110237 #include "ipc.h"
37*5184Sek110237 #include "eventgen.h"
38*5184Sek110237 #include "utils.h"
39*5184Sek110237 
40*5184Sek110237 /*
41*5184Sek110237  * Routines to access high resolution system time, initialize and
42*5184Sek110237  * shutdown filebench, obtain system generated random numbers from
43*5184Sek110237  * "urandom", log filebench run progress and errors, and access system
44*5184Sek110237  * information strings.
45*5184Sek110237  */
46*5184Sek110237 
47*5184Sek110237 
48*5184Sek110237 #if !defined(sun) && defined(USE_RDTSC)
49*5184Sek110237 /*
50*5184Sek110237  * Lets us use the rdtsc instruction to get highres time.
51*5184Sek110237  * Thanks to libmicro
52*5184Sek110237  */
53*5184Sek110237 uint64_t	cpu_hz = 0;
54*5184Sek110237 
55*5184Sek110237 /*
56*5184Sek110237  * Uses the rdtsc instruction to get high resolution (cpu
57*5184Sek110237  * clock ticks) time. Only used for non Sun compiles.
58*5184Sek110237  */
59*5184Sek110237 __inline__ long long
60*5184Sek110237 rdtsc(void)
61*5184Sek110237 {
62*5184Sek110237 	unsigned long long x;
63*5184Sek110237 	__asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
64*5184Sek110237 	return (x);
65*5184Sek110237 }
66*5184Sek110237 
67*5184Sek110237 /*
68*5184Sek110237  * Get high resolution time in nanoseconds. This is the version
69*5184Sek110237  * used when not compiled for Sun systems. It uses rdtsc call to
70*5184Sek110237  * get clock ticks and converts to nanoseconds
71*5184Sek110237  */
72*5184Sek110237 uint64_t
73*5184Sek110237 gethrtime(void)
74*5184Sek110237 {
75*5184Sek110237 	uint64_t hrt;
76*5184Sek110237 
77*5184Sek110237 	/* convert to nanosecs and return */
78*5184Sek110237 	hrt = 1000000000UL * rdtsc() / cpu_hz;
79*5184Sek110237 	return (hrt);
80*5184Sek110237 }
81*5184Sek110237 
82*5184Sek110237 /*
83*5184Sek110237  * Gets CPU clock frequency in MHz from cpuinfo file.
84*5184Sek110237  * Converts to cpu_hz and stores in cpu_hz global uint64_t.
85*5184Sek110237  * Only used for non Sun compiles.
86*5184Sek110237  */
87*5184Sek110237 static uint64_t
88*5184Sek110237 parse_cpu_hz(void)
89*5184Sek110237 {
90*5184Sek110237 	/*
91*5184Sek110237 	 * Parse the following from /proc/cpuinfo.
92*5184Sek110237 	 * cpu MHz		: 2191.563
93*5184Sek110237 	 */
94*5184Sek110237 	FILE *cpuinfo;
95*5184Sek110237 	double hertz = -1;
96*5184Sek110237 	uint64_t hz;
97*5184Sek110237 
98*5184Sek110237 	if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) {
99*5184Sek110237 		filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s",
100*5184Sek110237 		    strerror(errno));
101*5184Sek110237 		filebench_shutdown(1);
102*5184Sek110237 	}
103*5184Sek110237 	while (!feof(cpuinfo)) {
104*5184Sek110237 		char buffer[80];
105*5184Sek110237 
106*5184Sek110237 		fgets(buffer, 80, cpuinfo);
107*5184Sek110237 		if (strlen(buffer) == 0) continue;
108*5184Sek110237 		if (strncasecmp(buffer, "cpu MHz", 7) == 0) {
109*5184Sek110237 			char *token = strtok(buffer, ":");
110*5184Sek110237 
111*5184Sek110237 			if (token != NULL) {
112*5184Sek110237 				token = strtok((char *)NULL, ":");
113*5184Sek110237 				hertz = strtod(token, NULL);
114*5184Sek110237 			}
115*5184Sek110237 			break;
116*5184Sek110237 		}
117*5184Sek110237 	}
118*5184Sek110237 	printf("CPU Mhz %9.6f, sysconf:%ld\n", hertz, sysconf(_SC_CLK_TCK));
119*5184Sek110237 	hz = hertz * 1000000;
120*5184Sek110237 
121*5184Sek110237 	return (hz);
122*5184Sek110237 }
123*5184Sek110237 
124*5184Sek110237 #elif !defined(sun)
125*5184Sek110237 
126*5184Sek110237 /*
127*5184Sek110237  * Get high resolution time in nanoseconds. This is the version
128*5184Sek110237  * used if compiled for Sun systems. It calls gettimeofday
129*5184Sek110237  * to get current time and converts it to nanoseconds.
130*5184Sek110237  */
131*5184Sek110237 uint64_t
132*5184Sek110237 gethrtime(void)
133*5184Sek110237 {
134*5184Sek110237 	struct timeval tv;
135*5184Sek110237 	uint64_t hrt;
136*5184Sek110237 
137*5184Sek110237 	gettimeofday(&tv, NULL);
138*5184Sek110237 
139*5184Sek110237 	hrt = (uint64_t)tv.tv_sec * 1000000000UL +
140*5184Sek110237 	    (uint64_t)tv.tv_usec * 1000UL;
141*5184Sek110237 	return (hrt);
142*5184Sek110237 }
143*5184Sek110237 #endif
144*5184Sek110237 
145*5184Sek110237 static int urandomfd;
146*5184Sek110237 
147*5184Sek110237 /*
148*5184Sek110237  * Main filebench initialization. Opens the random number
149*5184Sek110237  * "device" file or shuts down the run if one is not found.
150*5184Sek110237  * Sets the cpu clock frequency variable or shuts down the
151*5184Sek110237  * run if one is not found.
152*5184Sek110237  */
153*5184Sek110237 void
154*5184Sek110237 filebench_init(void)
155*5184Sek110237 {
156*5184Sek110237 	/* open the "urandom" random number device file */
157*5184Sek110237 	if ((urandomfd = open("/dev/urandom", O_RDONLY)) < 0) {
158*5184Sek110237 		filebench_log(LOG_ERROR, "open /dev/urandom failed: %s",
159*5184Sek110237 		    strerror(errno));
160*5184Sek110237 		filebench_shutdown(1);
161*5184Sek110237 	}
162*5184Sek110237 #if defined(USE_RDTSC) && (LINUX_PORT)
163*5184Sek110237 	cpu_hz = parse_cpu_hz();
164*5184Sek110237 	if (cpu_hz <= 0) {
165*5184Sek110237 		filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s",
166*5184Sek110237 		    strerror(errno));
167*5184Sek110237 		filebench_shutdown(1);
168*5184Sek110237 	}
169*5184Sek110237 #endif /* USE_RDTSC */
170*5184Sek110237 
171*5184Sek110237 }
172*5184Sek110237 
173*5184Sek110237 /*
174*5184Sek110237  * Reads a 64 bit random number from the urandom "file".
175*5184Sek110237  * Shuts down the run if the read fails. Otherwise returns
176*5184Sek110237  * the random number after rounding it off by "round".
177*5184Sek110237  * Returns 0 on success, -1 on failure.
178*5184Sek110237  */
179*5184Sek110237 int
180*5184Sek110237 filebench_randomno64(uint64_t *randp, uint64_t max, uint64_t round)
181*5184Sek110237 {
182*5184Sek110237 	uint64_t random;
183*5184Sek110237 
184*5184Sek110237 	/* check for round value too large */
185*5184Sek110237 	if (max <= round) {
186*5184Sek110237 		*randp = 0;
187*5184Sek110237 
188*5184Sek110237 		/* if it just fits, its ok, otherwise error */
189*5184Sek110237 		if (max == round)
190*5184Sek110237 			return (0);
191*5184Sek110237 		else
192*5184Sek110237 			return (-1);
193*5184Sek110237 	}
194*5184Sek110237 
195*5184Sek110237 	if (read(urandomfd, &random,
196*5184Sek110237 	    sizeof (uint64_t)) != sizeof (uint64_t)) {
197*5184Sek110237 		filebench_log(LOG_ERROR, "read /dev/urandom failed: %s",
198*5184Sek110237 		    strerror(errno));
199*5184Sek110237 		filebench_shutdown(1);
200*5184Sek110237 	}
201*5184Sek110237 
202*5184Sek110237 	/* clip with max and optionally round */
203*5184Sek110237 	max -= round;
204*5184Sek110237 	random = random / (FILEBENCH_RANDMAX64 / max);
205*5184Sek110237 	if (round) {
206*5184Sek110237 		random = random / round;
207*5184Sek110237 		random *= round;
208*5184Sek110237 	}
209*5184Sek110237 	if (random > max)
210*5184Sek110237 		random = max;
211*5184Sek110237 
212*5184Sek110237 	*randp = random;
213*5184Sek110237 	return (0);
214*5184Sek110237 }
215*5184Sek110237 
216*5184Sek110237 
217*5184Sek110237 /*
218*5184Sek110237  * Reads a 32 bit random number from the urandom "file".
219*5184Sek110237  * Shuts down the run if the read fails. Otherwise returns
220*5184Sek110237  * the random number after rounding it off by "round".
221*5184Sek110237  * Returns 0 on success, -1 on failure.
222*5184Sek110237  */
223*5184Sek110237 int
224*5184Sek110237 filebench_randomno32(uint32_t *randp, uint32_t max, uint32_t round)
225*5184Sek110237 {
226*5184Sek110237 	uint32_t random;
227*5184Sek110237 
228*5184Sek110237 	/* check for round value too large */
229*5184Sek110237 	if (max <= round) {
230*5184Sek110237 		*randp = 0;
231*5184Sek110237 
232*5184Sek110237 		/* if it just fits, its ok, otherwise error */
233*5184Sek110237 		if (max == round)
234*5184Sek110237 			return (0);
235*5184Sek110237 		else
236*5184Sek110237 			return (-1);
237*5184Sek110237 	}
238*5184Sek110237 
239*5184Sek110237 	if (read(urandomfd, &random,
240*5184Sek110237 	    sizeof (uint32_t)) != sizeof (uint32_t)) {
241*5184Sek110237 		filebench_log(LOG_ERROR, "read /dev/urandom failed: %s",
242*5184Sek110237 		    strerror(errno));
243*5184Sek110237 		filebench_shutdown(1);
244*5184Sek110237 	}
245*5184Sek110237 
246*5184Sek110237 	/* clip with max and optionally round */
247*5184Sek110237 	max -= round;
248*5184Sek110237 	random = random / (FILEBENCH_RANDMAX32 / max);
249*5184Sek110237 	if (round) {
250*5184Sek110237 		random = random / round;
251*5184Sek110237 		random *= round;
252*5184Sek110237 	}
253*5184Sek110237 	if (random > max)
254*5184Sek110237 		random = max;
255*5184Sek110237 
256*5184Sek110237 	*randp = random;
257*5184Sek110237 	return (0);
258*5184Sek110237 }
259*5184Sek110237 
260*5184Sek110237 extern int lex_lineno;
261*5184Sek110237 
262*5184Sek110237 /*
263*5184Sek110237  * Writes a message consisting of information formated by
264*5184Sek110237  * "fmt" to the log file, dump file or stdout.  The supplied
265*5184Sek110237  * "level" argument determines which file to write to and
266*5184Sek110237  * what other actions to take. The level LOG_LOG writes to
267*5184Sek110237  * the "log" file, and will open the file on the first
268*5184Sek110237  * invocation. The level LOG_DUMP writes to the "dump" file,
269*5184Sek110237  * and will open it on the first invocation. Other levels
270*5184Sek110237  * print to the stdout device, with the amount of information
271*5184Sek110237  * dependent on the error level and the current error level
272*5184Sek110237  * setting in filebench_shm->debug_level.
273*5184Sek110237  */
274*5184Sek110237 void filebench_log
275*5184Sek110237 __V((int level, const char *fmt, ...))
276*5184Sek110237 {
277*5184Sek110237 	va_list args;
278*5184Sek110237 	hrtime_t now;
279*5184Sek110237 	char line[131072];
280*5184Sek110237 	char buf[131072];
281*5184Sek110237 
282*5184Sek110237 	if (level == LOG_FATAL)
283*5184Sek110237 		goto fatal;
284*5184Sek110237 
285*5184Sek110237 	/* open logfile if not already open and writing to it */
286*5184Sek110237 	if ((level == LOG_LOG) &&
287*5184Sek110237 	    (filebench_shm->log_fd < 0)) {
288*5184Sek110237 		char path[MAXPATHLEN];
289*5184Sek110237 		char *s;
290*5184Sek110237 
291*5184Sek110237 		(void) strcpy(path, filebench_shm->fscriptname);
292*5184Sek110237 		if ((s = strstr(path, ".f")))
293*5184Sek110237 			*s = 0;
294*5184Sek110237 		else
295*5184Sek110237 			(void) strcpy(path, "filebench");
296*5184Sek110237 
297*5184Sek110237 		(void) strcat(path, ".csv");
298*5184Sek110237 
299*5184Sek110237 		filebench_shm->log_fd =
300*5184Sek110237 		    open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
301*5184Sek110237 	}
302*5184Sek110237 
303*5184Sek110237 	/*
304*5184Sek110237 	 * if logfile still not open, switch to LOG_ERROR level so
305*5184Sek110237 	 * it gets reported to stdout
306*5184Sek110237 	 */
307*5184Sek110237 	if ((level == LOG_LOG) &&
308*5184Sek110237 	    (filebench_shm->log_fd < 0)) {
309*5184Sek110237 		(void) snprintf(line, sizeof (line),  "Open logfile failed: %s",
310*5184Sek110237 		    strerror(errno));
311*5184Sek110237 		level = LOG_ERROR;
312*5184Sek110237 	}
313*5184Sek110237 
314*5184Sek110237 	/* open dumpfile if not already open and writing to it */
315*5184Sek110237 	if ((level == LOG_DUMP) &&
316*5184Sek110237 	    (*filebench_shm->dump_filename == 0))
317*5184Sek110237 		return;
318*5184Sek110237 
319*5184Sek110237 	if ((level == LOG_DUMP) &&
320*5184Sek110237 	    (filebench_shm->dump_fd < 0)) {
321*5184Sek110237 
322*5184Sek110237 		filebench_shm->dump_fd =
323*5184Sek110237 		    open(filebench_shm->dump_filename,
324*5184Sek110237 		    O_RDWR | O_CREAT | O_TRUNC, 0666);
325*5184Sek110237 	}
326*5184Sek110237 
327*5184Sek110237 	if ((level == LOG_DUMP) &&
328*5184Sek110237 	    (filebench_shm->dump_fd < 0)) {
329*5184Sek110237 		(void) snprintf(line, sizeof (line), "Open logfile failed: %s",
330*5184Sek110237 		    strerror(errno));
331*5184Sek110237 		level = LOG_ERROR;
332*5184Sek110237 	}
333*5184Sek110237 
334*5184Sek110237 	/* Only log greater than debug setting */
335*5184Sek110237 	if ((level != LOG_DUMP) && (level != LOG_LOG) &&
336*5184Sek110237 	    (level > filebench_shm->debug_level))
337*5184Sek110237 		return;
338*5184Sek110237 
339*5184Sek110237 	now = gethrtime();
340*5184Sek110237 
341*5184Sek110237 fatal:
342*5184Sek110237 
343*5184Sek110237 #ifdef __STDC__
344*5184Sek110237 	va_start(args, fmt);
345*5184Sek110237 #else
346*5184Sek110237 	char *fmt;
347*5184Sek110237 	va_start(args);
348*5184Sek110237 	fmt = va_arg(args, char *);
349*5184Sek110237 #endif
350*5184Sek110237 
351*5184Sek110237 	(void) vsprintf(line, fmt, args);
352*5184Sek110237 
353*5184Sek110237 	va_end(args);
354*5184Sek110237 
355*5184Sek110237 	if (level == LOG_FATAL) {
356*5184Sek110237 		(void) fprintf(stdout, "%s\n", line);
357*5184Sek110237 		return;
358*5184Sek110237 	}
359*5184Sek110237 
360*5184Sek110237 	/* Serialize messages to log */
361*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->msg_lock);
362*5184Sek110237 
363*5184Sek110237 	if (level == LOG_LOG) {
364*5184Sek110237 		if (filebench_shm->log_fd > 0) {
365*5184Sek110237 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
366*5184Sek110237 			(void) write(filebench_shm->log_fd, buf, strlen(buf));
367*5184Sek110237 			(void) fsync(filebench_shm->log_fd);
368*5184Sek110237 		}
369*5184Sek110237 
370*5184Sek110237 	} else if (level == LOG_DUMP) {
371*5184Sek110237 		if (filebench_shm->dump_fd != -1) {
372*5184Sek110237 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
373*5184Sek110237 			(void) write(filebench_shm->dump_fd, buf, strlen(buf));
374*5184Sek110237 			(void) fsync(filebench_shm->dump_fd);
375*5184Sek110237 		}
376*5184Sek110237 
377*5184Sek110237 	} else if (filebench_shm->debug_level > LOG_INFO) {
378*5184Sek110237 		(void) fprintf(stdout, "%5ld: %4.3f: %s",
379*5184Sek110237 		    pid, (now - filebench_shm->epoch) / FSECS,
380*5184Sek110237 		    line);
381*5184Sek110237 
382*5184Sek110237 	} else {
383*5184Sek110237 		(void) fprintf(stdout, "%4.3f: %s",
384*5184Sek110237 		    (now - filebench_shm->epoch) / FSECS,
385*5184Sek110237 		    line);
386*5184Sek110237 	}
387*5184Sek110237 
388*5184Sek110237 	if (level == LOG_ERROR) {
389*5184Sek110237 		(void) fprintf(stdout, " on line %d", lex_lineno);
390*5184Sek110237 	}
391*5184Sek110237 
392*5184Sek110237 	if ((level != LOG_LOG) && (level != LOG_DUMP)) {
393*5184Sek110237 		(void) fprintf(stdout, "\n");
394*5184Sek110237 		(void) fflush(stdout);
395*5184Sek110237 	}
396*5184Sek110237 
397*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->msg_lock);
398*5184Sek110237 }
399*5184Sek110237 
400*5184Sek110237 /*
401*5184Sek110237  * Stops the run and exits filebench. If filebench is
402*5184Sek110237  * currently running a workload, calls procflow_shutdown()
403*5184Sek110237  * to stop the run. Also closes and deletes shared memory.
404*5184Sek110237  */
405*5184Sek110237 void
406*5184Sek110237 filebench_shutdown(int error) {
407*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Shutdown");
408*5184Sek110237 	(void) unlink("/tmp/filebench_shm");
409*5184Sek110237 	if (filebench_shm->allrunning)
410*5184Sek110237 		procflow_shutdown();
411*5184Sek110237 	filebench_shm->f_abort = 1;
412*5184Sek110237 	ipc_ismdelete();
413*5184Sek110237 	exit(error);
414*5184Sek110237 }
415*5184Sek110237 
416*5184Sek110237 /*
417*5184Sek110237  * Put the hostname in ${hostname}. The system supplied
418*5184Sek110237  * host name string is copied into an allocated string and
419*5184Sek110237  * the pointer to the string is placed in the supplied
420*5184Sek110237  * variable "var". If var->var_string already points to
421*5184Sek110237  * a string, the string is freed. The routine always
422*5184Sek110237  * returns zero (0).
423*5184Sek110237  */
424*5184Sek110237 var_t *
425*5184Sek110237 host_var(var_t *var)
426*5184Sek110237 {
427*5184Sek110237 	char hoststr[128];
428*5184Sek110237 
429*5184Sek110237 	(void) gethostname(hoststr, 128);
430*5184Sek110237 	if (var->var_string)
431*5184Sek110237 		free(var->var_string);
432*5184Sek110237 	var->var_string = fb_stralloc(hoststr);
433*5184Sek110237 	return (0);
434*5184Sek110237 }
435*5184Sek110237 
436*5184Sek110237 /*
437*5184Sek110237  * Put the date string in ${date}. The system supplied date is
438*5184Sek110237  * copied into an allocated string and the pointer to the string
439*5184Sek110237  * is placed in the supplied var_t's var_string. If
440*5184Sek110237  * var->var_string already points to a string, the string
441*5184Sek110237  * is freed. The routine always returns a pointer to the
442*5184Sek110237  * supplied var_t.
443*5184Sek110237  */
444*5184Sek110237 var_t *
445*5184Sek110237 date_var(var_t *var)
446*5184Sek110237 {
447*5184Sek110237 	char datestr[128];
448*5184Sek110237 #ifdef HAVE_CFTIME
449*5184Sek110237 	time_t t = time(NULL);
450*5184Sek110237 #else
451*5184Sek110237 	struct tm t;
452*5184Sek110237 #endif
453*5184Sek110237 
454*5184Sek110237 #ifdef HAVE_CFTIME
455*5184Sek110237 	cftime(datestr, "%y%m%d%H" "%M", &t);
456*5184Sek110237 #else
457*5184Sek110237 	(void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t);
458*5184Sek110237 #endif
459*5184Sek110237 
460*5184Sek110237 	if (var->var_string)
461*5184Sek110237 		free(var->var_string);
462*5184Sek110237 	var->var_string = fb_stralloc(datestr);
463*5184Sek110237 
464*5184Sek110237 	return (var);
465*5184Sek110237 }
466*5184Sek110237 
467*5184Sek110237 extern char *fscriptname;
468*5184Sek110237 
469*5184Sek110237 /*
470*5184Sek110237  * Put the script name in ${script}. The path name of the script
471*5184Sek110237  * used with this filebench run trimmed of the trailing ".f" and
472*5184Sek110237  * all leading subdirectories. The remaining script name is
473*5184Sek110237  * copied into the var_string field of the supplied variable
474*5184Sek110237  * "var". The routine always returns a pointer to the supplied
475*5184Sek110237  * var_t.
476*5184Sek110237  */
477*5184Sek110237 var_t *
478*5184Sek110237 script_var(var_t *var)
479*5184Sek110237 {
480*5184Sek110237 	char *scriptstr;
481*5184Sek110237 	char *f = fb_stralloc(fscriptname);
482*5184Sek110237 
483*5184Sek110237 	/* Trim the .f suffix */
484*5184Sek110237 	for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) {
485*5184Sek110237 		if (*scriptstr == '.') {
486*5184Sek110237 			*scriptstr = 0;
487*5184Sek110237 			break;
488*5184Sek110237 		}
489*5184Sek110237 	}
490*5184Sek110237 
491*5184Sek110237 	var->var_string = fb_stralloc(basename(f));
492*5184Sek110237 	free(f);
493*5184Sek110237 
494*5184Sek110237 	return (var);
495*5184Sek110237 }
496