• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: MIT or GPL-2.0-only
2 
3 #include "config.h"
4 #include "ublksrv_tgt.h"
5 
6 /* per-task variable */
7 static pthread_mutex_t jbuf_lock;
8 static int jbuf_size = 0;
9 static int queues_stored = 0;
10 static char *jbuf = NULL;
11 
12 struct ublksrv_queue_info {
13 	const struct ublksrv_dev *dev;
14 	int qid;
15 	pthread_t thread;
16 };
17 
18 
19 /********************cmd handling************************/
20 static char *full_cmd;
21 static struct ublksrv_tgt_type *tgt_list[UBLKSRV_TGT_TYPE_MAX] = {};
22 
ublk_json_write_dev_info(struct ublksrv_dev * dev,char ** jbuf,int * len)23 int ublk_json_write_dev_info(struct ublksrv_dev *dev, char **jbuf, int *len)
24 {
25 	int ret = 0;
26 
27 	do {
28 		ret = ublksrv_json_write_dev_info(ublksrv_get_ctrl_dev(dev),
29 				*jbuf, *len);
30 		if (ret < 0)
31 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
32 	} while (ret < 0);
33 
34 	return ret;
35 }
36 
ublk_json_write_params(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublk_params * p)37 int ublk_json_write_params(struct ublksrv_dev *dev, char **jbuf, int *len,
38 		const struct ublk_params *p)
39 {
40 	int ret = 0;
41 
42 	do {
43 		ret = ublksrv_json_write_params(p, *jbuf, *len);
44 		if (ret < 0)
45 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
46 	} while (ret < 0);
47 
48 	return ret;
49 }
50 
ublk_json_write_target_base(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublksrv_tgt_base_json * tgt)51 int ublk_json_write_target_base(struct ublksrv_dev *dev, char **jbuf, int *len,
52 		const struct ublksrv_tgt_base_json *tgt)
53 {
54 	int ret = 0;
55 
56 	do {
57 		ret = ublksrv_json_write_target_base_info(*jbuf, *len, tgt);
58 		if (ret < 0)
59 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
60 	} while (ret < 0);
61 
62 	return ret;
63 
64 }
65 
ublk_json_write_tgt_str(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,const char * val)66 int ublk_json_write_tgt_str(struct ublksrv_dev *dev, char **jbuf,
67 		int *len, const char *name, const char *val)
68 {
69 	int ret = 0;
70 
71 	do {
72 		if (val)
73 			ret = ublksrv_json_write_target_str_info(*jbuf,
74 				*len, name, val);
75 
76 		if (ret < 0)
77 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
78 	} while (ret < 0);
79 
80 	return ret;
81 }
82 
ublk_json_write_tgt_ulong(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,unsigned long val)83 int ublk_json_write_tgt_ulong(struct ublksrv_dev *dev, char **jbuf,
84 		int *len, const char *name, unsigned long val)
85 {
86 	int ret = 0;
87 
88 	do {
89 		ret = ublksrv_json_write_target_ulong_info(*jbuf,
90 				*len, name, val);
91 		if (ret < 0)
92 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
93 	} while (ret < 0);
94 
95 	return ret;
96 }
97 
ublk_json_write_tgt_long(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,long val)98 int ublk_json_write_tgt_long(struct ublksrv_dev *dev, char **jbuf,
99 		int *len, const char *name, long val)
100 {
101 	int ret = 0;
102 
103 	do {
104 		ret = ublksrv_json_write_target_long_info(*jbuf,
105 				*len, name, val);
106 		if (ret < 0)
107 			*jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
108 	} while (ret < 0);
109 
110 	return ret;
111 }
112 
ublksrv_find_tgt_type(const char * name)113 static const struct ublksrv_tgt_type *ublksrv_find_tgt_type(const char *name)
114 {
115 	int i;
116 
117 	for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
118 		const struct ublksrv_tgt_type *type = tgt_list[i];
119 
120 		if (type == NULL)
121 			continue;
122 
123 		if (!strcmp(type->name, name))
124 			return type;
125 	}
126 
127 	return NULL;
128 }
129 
ublksrv_for_each_tgt_type(void (* handle_tgt_type)(unsigned idx,const struct ublksrv_tgt_type * type,void * data),void * data)130 static void ublksrv_for_each_tgt_type(void (*handle_tgt_type)(unsigned idx,
131 			const struct ublksrv_tgt_type *type, void *data),
132 		void *data)
133 {
134 	int i;
135 
136 	for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
137                 const struct ublksrv_tgt_type  *type = tgt_list[i];
138 
139 		if (!type)
140 			continue;
141 		handle_tgt_type(i, type, data);
142 	}
143 }
144 
ublksrv_register_tgt_type(struct ublksrv_tgt_type * type)145 int ublksrv_register_tgt_type(struct ublksrv_tgt_type *type)
146 {
147 	if (type->type < UBLKSRV_TGT_TYPE_MAX && !tgt_list[type->type]) {
148 		tgt_list[type->type] = type;
149 		return 0;
150 	}
151 	return -1;
152 }
153 
ublksrv_unregister_tgt_type(struct ublksrv_tgt_type * type)154 void ublksrv_unregister_tgt_type(struct ublksrv_tgt_type *type)
155 {
156 	if (type->type < UBLKSRV_TGT_TYPE_MAX && tgt_list[type->type]) {
157 		tgt_list[type->type] = NULL;
158 	}
159 }
160 
161 
mprintf(const char * fmt,...)162 static char *mprintf(const char *fmt, ...)
163 {
164 	va_list args;
165 	char *str;
166 	int ret;
167 
168 	va_start(args, fmt);
169 	ret = vasprintf(&str, fmt, args);
170 	va_end(args);
171 
172 	if (ret < 0) {
173 		return NULL;
174 	}
175 
176 	return str;
177 }
178 
pop_cmd(int * argc,char * argv[])179 static char *pop_cmd(int *argc, char *argv[])
180 {
181 	char *cmd = argv[1];
182 	if (*argc < 2) {
183 		return NULL;
184 	}
185 
186 	memmove(&argv[1], &argv[2], *argc * sizeof(argv[0]));
187 	(*argc)--;
188 
189 	full_cmd = mprintf("%s %s", full_cmd, cmd);
190 	return cmd;
191 }
192 
start_daemon(void (* child_fn)(void *),void * data)193 static int start_daemon(void (*child_fn)(void *), void *data)
194 {
195 	char path[PATH_MAX];
196 	int fd;
197 	char *res;
198 
199 	if (setsid() == -1)
200 		return -1;
201 
202 	res = getcwd(path, PATH_MAX);
203 	if (!res)
204 		ublk_err("%s: %d getcwd failed %m\n", __func__, __LINE__);
205 
206 	switch (fork()) {
207 	case -1: return -1;
208 	case 0:  break;
209 	default: _exit(EXIT_SUCCESS);
210 	}
211 
212 	if (chdir(path) != 0)
213 		ublk_err("%s: %d chdir failed %m\n", __func__, __LINE__);
214 
215 	close(STDIN_FILENO);
216 	fd = open("/dev/null", O_RDWR);
217 	if (fd != STDIN_FILENO)
218 		return -1;
219 	if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
220 		return -1;
221 	if (dup2(fd, STDERR_FILENO) != STDERR_FILENO)
222 		return -1;
223 
224 	child_fn(data);
225 	return 0;
226 }
227 
__ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)228 char *__ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
229 {
230 	if (jbuf == NULL) {
231 		jbuf_size = 1024;
232 		jbuf = (char *)realloc((void *)jbuf, jbuf_size);
233 	}
234 	*size = jbuf_size;
235 
236 	return jbuf;
237 }
238 
ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)239 char *ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
240 {
241 	char *buf;
242 
243 	pthread_mutex_lock(&jbuf_lock);
244 	buf = __ublksrv_tgt_return_json_buf(dev, size);
245 	pthread_mutex_unlock(&jbuf_lock);
246 
247 	return buf;
248 }
249 
__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev * dev,int * size)250 static char *__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev *dev, int *size)
251 {
252 	if (jbuf == NULL)
253 		jbuf_size = 1024;
254 	else
255 		jbuf_size += 1024;
256 
257 	jbuf = (char *)realloc((void *)jbuf, jbuf_size);
258 	*size = jbuf_size;
259 
260 	return jbuf;
261 }
262 
ublksrv_tgt_realloc_json_buf(struct ublksrv_dev * dev,int * size)263 char *ublksrv_tgt_realloc_json_buf(struct ublksrv_dev *dev, int *size)
264 {
265 	char *buf;
266 
267 	pthread_mutex_lock(&jbuf_lock);
268 	buf = __ublksrv_tgt_realloc_json_buf(dev, size);
269 	pthread_mutex_unlock(&jbuf_lock);
270 
271 	return buf;
272 }
273 
ublksrv_tgt_store_dev_data(const struct ublksrv_dev * dev,const char * buf)274 static int ublksrv_tgt_store_dev_data(const struct ublksrv_dev *dev,
275 		const char *buf)
276 {
277 	int ret;
278 	int len = ublksrv_json_get_length(buf);
279 	int fd = ublksrv_get_pidfile_fd(dev);
280 
281 	if (fd < 0) {
282 		ublk_err( "fail to get fd of pid file, ret %d\n",
283 				fd);
284 		return fd;
285 	}
286 
287 	ret = pwrite(fd, buf, len, JSON_OFFSET);
288 	if (ret <= 0)
289 		ublk_err( "fail to write json data to pid file, ret %d\n",
290 				ret);
291 
292 	return ret;
293 }
294 
ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev * cdev)295 static char *ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev *cdev)
296 {
297 	const struct ublksrv_ctrl_dev_info *info =
298 		ublksrv_ctrl_get_dev_info(cdev);
299 	int dev_id = info->dev_id;
300 	struct stat st;
301 	char pid_file[256];
302 	char *buf;
303 	int size, fd, ret;
304 	const char *run_dir = ublksrv_ctrl_get_run_dir(cdev);
305 
306 	if (!run_dir)
307 		return 0;
308 
309 	snprintf(pid_file, 256, "%s/%d.pid", run_dir, dev_id);
310 	fd = open(pid_file, O_RDONLY);
311 
312 	if (fd <= 0)
313 		return NULL;
314 
315 	if (fstat(fd, &st) < 0)
316 		return NULL;
317 
318 	if (st.st_size <=  JSON_OFFSET)
319 		return NULL;
320 
321 	size = st.st_size - JSON_OFFSET;
322 	buf = (char *)malloc(size);
323 	ret = pread(fd, buf, size, JSON_OFFSET);
324 	if (ret <= 0)
325 		fprintf(stderr, "fail to read json from %s ret %d\n",
326 				pid_file, ret);
327 	close(fd);
328 
329 	return buf;
330 }
331 
ublksrv_io_handler_fn(void * data)332 static void *ublksrv_io_handler_fn(void *data)
333 {
334 	struct ublksrv_queue_info *info = (struct ublksrv_queue_info *)data;
335 	const struct ublksrv_dev *dev = info->dev;
336 	const struct ublksrv_ctrl_dev *cdev = ublksrv_get_ctrl_dev(dev);
337 	const struct ublksrv_ctrl_dev_info *dinfo =
338 		ublksrv_ctrl_get_dev_info(cdev);
339 	unsigned dev_id = dinfo->dev_id;
340 	unsigned short q_id = info->qid;
341 	const struct ublksrv_queue *q;
342 	int ret;
343 	int buf_size;
344 	char *buf;
345 	const char *jbuf;
346 
347 	pthread_mutex_lock(&jbuf_lock);
348 
349 	if (!ublksrv_is_recovering(cdev)) {
350 		do {
351 			buf = __ublksrv_tgt_realloc_json_buf(dev, &buf_size);
352 			ret = ublksrv_json_write_queue_info(cdev, buf, buf_size,
353 					q_id, ublksrv_gettid());
354 		} while (ret < 0);
355 		jbuf = buf;
356 	} else {
357 		jbuf = ublksrv_ctrl_get_recovery_jbuf(cdev);
358 	}
359 	queues_stored++;
360 
361 	/*
362 	 * A bit ugly to store json buffer to pid file here, but no easy
363 	 * way to do it in control task side, so far, so good
364 	 */
365 	if (queues_stored == dinfo->nr_hw_queues)
366 		ublksrv_tgt_store_dev_data(dev, jbuf);
367 	pthread_mutex_unlock(&jbuf_lock);
368 
369 	q = ublksrv_queue_init(dev, q_id, NULL);
370 	if (!q) {
371 		ublk_err("ublk dev %d queue %d init queue failed",
372 				dev_id, q_id);
373 		return NULL;
374 	}
375 
376 	ublk_log("tid %d: ublk dev %d queue %d started", ublksrv_gettid(),
377 			dev_id, q->q_id);
378 	do {
379 		if (ublksrv_process_io(q) < 0)
380 			break;
381 	} while (1);
382 
383 	ublk_log("ublk dev %d queue %d exited", dev_id, q->q_id);
384 	ublksrv_queue_deinit(q);
385 	return NULL;
386 }
387 
sig_handler(int sig)388 static void sig_handler(int sig)
389 {
390 	if (sig == SIGTERM)
391 		ublk_log("got TERM signal");
392 }
393 
setup_pthread_sigmask()394 static void setup_pthread_sigmask()
395 {
396 	sigset_t   signal_mask;
397 
398 	if (signal(SIGTERM, sig_handler) == SIG_ERR)
399 		return;
400 
401 	/* make sure SIGTERM won't be blocked */
402 	sigemptyset(&signal_mask);
403 	sigaddset(&signal_mask, SIGINT);
404 	sigaddset(&signal_mask, SIGTERM);
405 	pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
406 }
407 
408 /*
409  * Now STOP DEV ctrl command has been sent to /dev/ublk-control,
410  * and wait until all pending fetch commands are canceled
411  */
ublksrv_drain_fetch_commands(const struct ublksrv_dev * dev,struct ublksrv_queue_info * info)412 static void ublksrv_drain_fetch_commands(const struct ublksrv_dev *dev,
413 		struct ublksrv_queue_info *info)
414 {
415 	const struct ublksrv_ctrl_dev_info *dinfo =
416 		ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
417 	unsigned nr_queues = dinfo->nr_hw_queues;
418 	int i;
419 	void *ret;
420 
421 	for (i = 0; i < nr_queues; i++)
422 		pthread_join(info[i].thread, &ret);
423 }
424 
425 
ublksrv_io_handler(void * data)426 static void ublksrv_io_handler(void *data)
427 {
428 	const struct ublksrv_ctrl_dev *ctrl_dev = (struct ublksrv_ctrl_dev *)data;
429 	const struct ublksrv_ctrl_dev_info *dinfo =
430 		ublksrv_ctrl_get_dev_info(ctrl_dev);
431 	int dev_id = dinfo->dev_id;
432 	int i;
433 	char buf[32];
434 	const struct ublksrv_dev *dev;
435 	struct ublksrv_queue_info *info_array;
436 
437 	snprintf(buf, 32, "%s-%d", "ublksrvd", dev_id);
438 	openlog(buf, LOG_PID, LOG_USER);
439 
440 	ublk_log("start ublksrv io daemon %s\n", buf);
441 
442 	pthread_mutex_init(&jbuf_lock, NULL);
443 
444 	dev = ublksrv_dev_init(ctrl_dev);
445 	if (!dev) {
446 		ublk_err( "dev-%d start ubsrv failed", dev_id);
447 		goto out;
448 	}
449 
450 	setup_pthread_sigmask();
451 
452 	if (!(dinfo->flags & UBLK_F_UNPRIVILEGED_DEV))
453 		ublksrv_apply_oom_protection();
454 
455 	info_array = (struct ublksrv_queue_info *)calloc(sizeof(
456 				struct ublksrv_queue_info),
457 			dinfo->nr_hw_queues);
458 
459 	for (i = 0; i < dinfo->nr_hw_queues; i++) {
460 		info_array[i].dev = dev;
461 		info_array[i].qid = i;
462 		pthread_create(&info_array[i].thread, NULL,
463 				ublksrv_io_handler_fn,
464 				&info_array[i]);
465 	}
466 
467 	/* wait until we are terminated */
468 	ublksrv_drain_fetch_commands(dev, info_array);
469 	free(info_array);
470 	free(jbuf);
471 
472 	ublksrv_dev_deinit(dev);
473 out:
474 	ublk_log("end ublksrv io daemon");
475 	closelog();
476 }
477 
478 /* Not called from ublksrv daemon */
ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev * dev)479 static int ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev *dev)
480 {
481 	start_daemon(ublksrv_io_handler, (void *)dev);
482 	return 0;
483 }
484 
ublksrv_check_dev_data(const char * buf,int size)485 static int ublksrv_check_dev_data(const char *buf, int size)
486 {
487 	struct ublk_params p;
488 
489 	if (size < JSON_OFFSET)
490 		return -EINVAL;
491 
492 	return ublksrv_json_read_params(&p, &buf[JSON_OFFSET]);
493 }
494 
ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev * ctrl_dev,bool check_data)495 static int ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev *ctrl_dev,
496 		bool check_data)
497 {
498 	const char *run_dir = ublksrv_ctrl_get_run_dir(ctrl_dev);
499 	const struct ublksrv_ctrl_dev_info *info =
500 		ublksrv_ctrl_get_dev_info(ctrl_dev);
501 	int ret = -1, pid_fd;
502 	char path[256];
503 	char *buf = NULL;
504 	int size = JSON_OFFSET;
505 	int daemon_pid;
506 	struct stat st;
507 
508 	if (!run_dir)
509 		return -EINVAL;
510 
511 	snprintf(path, 256, "%s/%d.pid", run_dir, info->dev_id);
512 
513 	pid_fd = open(path, O_RDONLY);
514 	if (pid_fd < 0)
515 		goto out;
516 
517 	if (fstat(pid_fd, &st) < 0)
518 		goto out;
519 
520 	if (check_data)
521 		size = st.st_size;
522 	else
523 		size = JSON_OFFSET;
524 
525 	buf = (char *)malloc(size);
526 	if (read(pid_fd, buf, size) <= 0)
527 		goto out;
528 
529 	daemon_pid = strtol(buf, NULL, 10);
530 	if (daemon_pid < 0)
531 		goto out;
532 
533 	ret = kill(daemon_pid, 0);
534 	if (ret)
535 		goto out;
536 
537 	if (check_data) {
538 		ret = ublksrv_check_dev_data(buf, size);
539 		if (ret)
540 			goto out;
541 	}
542 	ret = daemon_pid;
543 out:
544 	if (pid_fd > 0)
545 		close(pid_fd);
546 	free(buf);
547 	return ret;
548 }
549 
550 /* Not called from ublksrv daemon */
ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev * ctrl_dev)551 static int ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev *ctrl_dev)
552 {
553 	int daemon_pid, cnt = 0;
554 
555 	/* wait until daemon is exited, or timeout after 3 seconds */
556 	do {
557 		daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, false);
558 		if (daemon_pid > 0) {
559 			usleep(100000);
560 			cnt++;
561 		}
562 	} while (daemon_pid > 0 && cnt < 30);
563 
564 	if (daemon_pid > 0)
565 		return -1;
566 
567 	return 0;
568 }
569 
570 /* Wait until ublk device is setup by udev */
ublksrv_check_dev(const struct ublksrv_ctrl_dev_info * info)571 static void ublksrv_check_dev(const struct ublksrv_ctrl_dev_info *info)
572 {
573 	unsigned int max_time = 1000000, wait = 0;
574 	char buf[64];
575 
576 	snprintf(buf, 64, "%s%d", "/dev/ublkc", info->dev_id);
577 
578 	while (wait < max_time) {
579 		int fd = open(buf, O_RDWR);
580 
581 		if (fd > 0) {
582 			close(fd);
583 			break;
584 		}
585 
586 		usleep(100000);
587 		wait += 100000;
588 	}
589 }
590 
ublksrv_start_daemon(struct ublksrv_ctrl_dev * ctrl_dev)591 static int ublksrv_start_daemon(struct ublksrv_ctrl_dev *ctrl_dev)
592 {
593 	const struct ublksrv_ctrl_dev_info *dinfo =
594 		ublksrv_ctrl_get_dev_info(ctrl_dev);
595 	int cnt = 0, daemon_pid, ret;
596 
597 	ublksrv_check_dev(dinfo);
598 
599 	ret = ublksrv_ctrl_get_affinity(ctrl_dev);
600 	if (ret < 0) {
601 		fprintf(stderr, "dev %d get affinity failed %d\n",
602 				dinfo->dev_id, ret);
603 		return -1;
604 	}
605 
606 	switch (fork()) {
607 	case -1:
608 		return -1;
609 	case 0:
610 		ublksrv_start_io_daemon(ctrl_dev);
611 		break;
612 	}
613 
614 	/* wait until daemon is started, or timeout after 3 seconds */
615 	do {
616 		daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, true);
617 		if (daemon_pid < 0) {
618 			usleep(100000);
619 			cnt++;
620 		}
621 	} while (daemon_pid < 0 && cnt < 30);
622 
623 	return daemon_pid;
624 }
625 
626 //todo: resolve stack usage warning for mkpath/__mkpath
627 #pragma GCC diagnostic push
628 #pragma GCC diagnostic ignored "-Wstack-usage="
__mkpath(char * dir,mode_t mode)629 static int __mkpath(char *dir, mode_t mode)
630 {
631 	struct stat sb;
632 	int ret;
633 	mode_t mask;
634 
635 	if (!dir)
636 		return -EINVAL;
637 
638 	if (!stat(dir, &sb))
639 		return 0;
640 
641 	__mkpath(dirname(strdupa(dir)), mode);
642 
643 	mask = umask(0);
644 	ret = mkdir(dir, mode);
645 	umask(mask);
646 
647 	return ret;
648 }
649 
mkpath(const char * dir)650 static int mkpath(const char *dir)
651 {
652 	return __mkpath(strdupa(dir), S_IRWXU | S_IRWXG | S_IRWXO);
653 }
654 #pragma GCC diagnostic pop
655 
ublksrv_tgt_set_params(struct ublksrv_ctrl_dev * cdev,const char * jbuf)656 static void ublksrv_tgt_set_params(struct ublksrv_ctrl_dev *cdev,
657 		const char *jbuf)
658 {
659 	const struct ublksrv_ctrl_dev_info *info =
660 		ublksrv_ctrl_get_dev_info(cdev);
661 	int dev_id = info->dev_id;
662 	struct ublk_params p;
663 	int ret;
664 
665 	ret = ublksrv_json_read_params(&p, jbuf);
666 	if (ret >= 0) {
667 		ret = ublksrv_ctrl_set_params(cdev, &p);
668 		if (ret)
669 			fprintf(stderr, "set param for dev %d failed %d\n",
670 					dev_id, ret);
671 	} else {
672 		fprintf(stderr, "params not found for dev %d failed %d\n",
673 				dev_id, ret);
674 	}
675 }
676 
cmd_dev_add(int argc,char * argv[])677 static int cmd_dev_add(int argc, char *argv[])
678 {
679 	static const struct option longopts[] = {
680 		{ "type",		1,	NULL, 't' },
681 		{ "number",		1,	NULL, 'n' },
682 		{ "queues",		1,	NULL, 'q' },
683 		{ "depth",		1,	NULL, 'd' },
684 		{ "zero_copy",		1,	NULL, 'z' },
685 		{ "uring_comp",		1,	NULL, 'u' },
686 		{ "need_get_data",	1,	NULL, 'g' },
687 		{ "user_recovery",	1,	NULL, 'r'},
688 		{ "user_recovery_fail_io",	1,	NULL, 'e'},
689 		{ "user_recovery_reissue",	1,	NULL, 'i'},
690 		{ "debug_mask",	1,	NULL, 0},
691 		{ "unprivileged",	0,	NULL, 0},
692 		{ "usercopy",	0,	NULL, 0},
693 		{ NULL }
694 	};
695 	struct ublksrv_dev_data data = {0};
696 	struct ublksrv_ctrl_dev *dev;
697 	const struct ublksrv_tgt_type *tgt_type;
698 	int opt, ret;
699 	int uring_comp = 0;
700 	int need_get_data = 0;
701 	int user_recovery = 0;
702 	int user_recovery_fail_io = 0;
703 	int user_recovery_reissue = 0;
704 	int unprivileged = 0;
705 	const char *dump_buf;
706 	int option_index = 0;
707 	unsigned int debug_mask = 0;
708 
709 	data.queue_depth = DEF_QD;
710 	data.nr_hw_queues = DEF_NR_HW_QUEUES;
711 	data.dev_id = -1;
712 	data.run_dir = UBLKSRV_PID_DIR;
713 
714 	mkpath(data.run_dir);
715 
716 	while ((opt = getopt_long(argc, argv, "-:t:n:d:q:u:g:r:e:i:z",
717 				  longopts, &option_index)) != -1) {
718 		switch (opt) {
719 		case 'n':
720 			data.dev_id = strtol(optarg, NULL, 10);
721 			break;
722 		case 't':
723 			data.tgt_type = optarg;
724 			break;
725 		case 'z':
726 			data.flags |= UBLK_F_SUPPORT_ZERO_COPY;
727 			break;
728 		case 'q':
729 			data.nr_hw_queues = strtol(optarg, NULL, 10);
730 			break;
731 		case 'd':
732 			data.queue_depth = strtol(optarg, NULL, 10);
733 			break;
734 		case 'u':
735 			uring_comp = strtol(optarg, NULL, 10);
736 			break;
737 		case 'g':
738 			need_get_data = strtol(optarg, NULL, 10);
739 			break;
740 		case 'r':
741 			user_recovery = strtol(optarg, NULL, 10);
742 			break;
743 		case 'e':
744 			user_recovery_fail_io = strtol(optarg, NULL, 10);
745 			break;
746 		case 'i':
747 			user_recovery_reissue = strtol(optarg, NULL, 10);
748 			break;
749 		case 0:
750 			if (!strcmp(longopts[option_index].name, "debug_mask"))
751 				debug_mask = strtol(optarg, NULL, 16);
752 			if (!strcmp(longopts[option_index].name, "unprivileged"))
753 				unprivileged = 1;
754 			if (!strcmp(longopts[option_index].name, "usercopy"))
755 				data.flags |= UBLK_F_USER_COPY;
756 			break;
757 		}
758 	}
759 
760 	ublk_set_debug_mask(debug_mask);
761 
762 	data.max_io_buf_bytes = DEF_BUF_SIZE;
763 	if (data.nr_hw_queues > MAX_NR_HW_QUEUES)
764 		data.nr_hw_queues = MAX_NR_HW_QUEUES;
765 	if (data.queue_depth > MAX_QD)
766 		data.queue_depth = MAX_QD;
767 	if (uring_comp)
768 		data.flags |= UBLK_F_URING_CMD_COMP_IN_TASK;
769 	if (need_get_data)
770 		data.flags |= UBLK_F_NEED_GET_DATA;
771 	if (user_recovery)
772 		data.flags |= UBLK_F_USER_RECOVERY;
773 	if (user_recovery_fail_io)
774 		data.flags |= UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_FAIL_IO;
775 	if (user_recovery_reissue)
776 		data.flags |= UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE;
777 	if (unprivileged)
778 		data.flags |= UBLK_F_UNPRIVILEGED_DEV;
779 
780 	if (data.tgt_type == NULL) {
781 		fprintf(stderr, "no dev type specified\n");
782 		return -EINVAL;
783 	}
784 	tgt_type = ublksrv_find_tgt_type(data.tgt_type);
785 	if (tgt_type == NULL) {
786 		fprintf(stderr, "unknown dev type: %s\n", data.tgt_type);
787 		return -EINVAL;
788 	}
789 	data.tgt_ops = tgt_type;
790 	data.flags |= tgt_type->ublk_flags;
791 	data.ublksrv_flags |= tgt_type->ublksrv_flags;
792 
793 	//optind = 0;	/* so that tgt code can parse their arguments */
794 	data.tgt_argc = argc;
795 	data.tgt_argv = argv;
796 	dev = ublksrv_ctrl_init(&data);
797 	if (!dev) {
798 		fprintf(stderr, "can't init dev %d\n", data.dev_id);
799 		return -EOPNOTSUPP;
800 	}
801 
802 	ret = ublksrv_ctrl_add_dev(dev);
803 	if (ret < 0) {
804 		fprintf(stderr, "can't add dev %d, ret %d\n", data.dev_id, ret);
805 		goto fail;
806 	}
807 
808 	{
809 		const struct ublksrv_ctrl_dev_info *info =
810 			ublksrv_ctrl_get_dev_info(dev);
811 		data.dev_id = info->dev_id;
812 	}
813 	ret = ublksrv_start_daemon(dev);
814 	if (ret <= 0) {
815 		fprintf(stderr, "start dev %d daemon failed, ret %d\n",
816 				data.dev_id, ret);
817 		goto fail_del_dev;
818 	}
819 
820 	dump_buf = ublksrv_tgt_get_dev_data(dev);
821 	ublksrv_tgt_set_params(dev, dump_buf);
822 
823 	ret = ublksrv_ctrl_start_dev(dev, ret);
824 	if (ret < 0) {
825 		fprintf(stderr, "start dev %d failed, ret %d\n", data.dev_id,
826 				ret);
827 		goto fail_stop_daemon;
828 	}
829 	ret = ublksrv_ctrl_get_info(dev);
830 	ublksrv_ctrl_dump(dev, dump_buf);
831 	ublksrv_ctrl_deinit(dev);
832 	return 0;
833 
834  fail_stop_daemon:
835 	ublksrv_stop_io_daemon(dev);
836  fail_del_dev:
837 	ublksrv_ctrl_del_dev(dev);
838  fail:
839 	ublksrv_ctrl_deinit(dev);
840 
841 	return ret;
842 }
843 
844 struct tgt_types_name {
845 	unsigned pos;
846 	char names[4096 - sizeof(unsigned)];
847 };
848 
collect_tgt_types(unsigned int idx,const struct ublksrv_tgt_type * type,void * pdata)849 static void collect_tgt_types(unsigned int idx,
850 		const struct ublksrv_tgt_type *type, void *pdata)
851 {
852 	struct tgt_types_name *data = (struct tgt_types_name *)pdata;
853 
854 	if (idx > 0)
855 		data->pos += snprintf(data->names + data->pos,
856                   sizeof(data->names) - data->pos, "|");
857 	data->pos += snprintf(data->names + data->pos,
858                 sizeof(data->names) - data->pos, "%s", type->name);
859 }
860 
show_tgt_add_usage(unsigned int idx,const struct ublksrv_tgt_type * type,void * data)861 static void show_tgt_add_usage(unsigned int idx,
862 		const struct ublksrv_tgt_type *type, void *data)
863 {
864 	if (type->usage_for_add)
865 		type->usage_for_add();
866 }
867 
cmd_dev_add_usage(const char * cmd)868 static void cmd_dev_add_usage(const char *cmd)
869 {
870 	struct tgt_types_name data = {
871 		.pos = 0,
872 	};
873 
874 	data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "{");
875 	ublksrv_for_each_tgt_type(collect_tgt_types, &data);
876 	data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "}");
877 
878 	printf("%s add -t %s\n", cmd, data.names);
879 	printf("\t-n DEV_ID -q NR_HW_QUEUES -d QUEUE_DEPTH\n");
880 	printf("\t-u URING_COMP -g NEED_GET_DATA -r USER_RECOVERY\n");
881 	printf("\t-i USER_RECOVERY_REISSUE -e USER_RECOVERY_FAIL_IO\n");
882 	printf("\t--debug_mask=0x{DBG_MASK} --unprivileged\n\n");
883 	printf("\ttarget specific command line:\n");
884 	ublksrv_for_each_tgt_type(show_tgt_add_usage, NULL);
885 }
886 
__cmd_dev_del(int number,bool log,bool async)887 static int __cmd_dev_del(int number, bool log, bool async)
888 {
889 	struct ublksrv_ctrl_dev *dev;
890 	int ret;
891 	struct ublksrv_dev_data data = {
892 		.dev_id = number,
893 		.run_dir = UBLKSRV_PID_DIR,
894 	};
895 
896 	dev = ublksrv_ctrl_init(&data);
897 	if (!dev) {
898 		fprintf(stderr, "ublksrv_ctrl_init failed id %d\n", number);
899 		return -EOPNOTSUPP;
900 	}
901 
902 	ret = ublksrv_ctrl_get_info(dev);
903 	if (ret < 0) {
904 		ret = 0;
905 		if (log)
906 			fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
907 		goto fail;
908 	}
909 
910 	ret = ublksrv_ctrl_stop_dev(dev);
911 	if (ret < 0) {
912 		fprintf(stderr, "stop dev %d failed\n", number);
913 		goto fail;
914 	}
915 
916 	ret = ublksrv_stop_io_daemon(dev);
917 	if (ret < 0)
918 		fprintf(stderr, "stop daemon %d failed\n", number);
919 
920 	if (async)
921 		ret = ublksrv_ctrl_del_dev_async(dev);
922 	else
923 		ret = ublksrv_ctrl_del_dev(dev);
924 	if (ret < 0) {
925 		fprintf(stderr, "delete dev %d failed %d\n", number, ret);
926 		goto fail;
927 	}
928 
929 fail:
930 	ublksrv_ctrl_deinit(dev);
931 	return ret;
932 }
933 
cmd_dev_del(int argc,char * argv[])934 static int cmd_dev_del(int argc, char *argv[])
935 {
936 	static const struct option longopts[] = {
937 		{ "number",		1,	NULL, 'n' },
938 		{ "all",		0,	NULL, 'a' },
939 		{ "async",		0,	NULL,  0  },
940 		{ NULL }
941 	};
942 	int number = -1;
943 	int opt, ret, i;
944 	unsigned async = 0;
945 	int option_index = 0;
946 
947 	while ((opt = getopt_long(argc, argv, "n:a",
948 				  longopts, &option_index)) != -1) {
949 		switch (opt) {
950 		case 'a':
951 			break;
952 
953 		case 'n':
954 			number = strtol(optarg, NULL, 10);
955 			break;
956 		case 0:
957 			if (!strcmp(longopts[option_index].name, "async"))
958 				async = 1;
959 		}
960 	}
961 
962 	if (number >= 0)
963 		return __cmd_dev_del(number, true, async);
964 
965 	for (i = 0; i < MAX_NR_UBLK_DEVS; i++) {
966 		ret = __cmd_dev_del(i, false, async);
967 		if (ret == -EOPNOTSUPP)
968 			return ret;
969 	}
970 
971 	return ret;
972 }
973 
cmd_dev_del_usage(const char * cmd)974 static void cmd_dev_del_usage(const char *cmd)
975 {
976 	printf("%s del -n DEV_ID [-a | --all]\n", cmd);
977 }
978 
list_one_dev(int number,bool log,bool verbose)979 static int list_one_dev(int number, bool log, bool verbose)
980 {
981 	struct ublksrv_dev_data data = {
982 		.dev_id = number,
983 		.run_dir = UBLKSRV_PID_DIR,
984 	};
985 	struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
986 	int ret;
987 
988 	if (!dev) {
989 		fprintf(stderr, "ublksrv_ctrl_init failed id %d\n", number);
990 		return -EOPNOTSUPP;
991 	}
992 	ret = ublksrv_ctrl_get_info(dev);
993 	if (ret < 0) {
994 		if (log)
995 			fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
996 	} else {
997 		const char *buf = ublksrv_tgt_get_dev_data(dev);
998 
999 		if (verbose)
1000 			ublksrv_json_dump(buf);
1001 		else
1002 			ublksrv_ctrl_dump(dev, buf);
1003 	}
1004 
1005 	ublksrv_ctrl_deinit(dev);
1006 
1007 	return ret;
1008 }
1009 
cmd_list_dev_info(int argc,char * argv[])1010 static int cmd_list_dev_info(int argc, char *argv[])
1011 {
1012 	static const struct option longopts[] = {
1013 		{ "number",		0,	NULL, 'n' },
1014 		{ "verbose",		0,	NULL, 'v' },
1015 		{ NULL }
1016 	};
1017 	int number = -1;
1018 	int opt, i;
1019 	bool verbose = false;
1020 
1021 	while ((opt = getopt_long(argc, argv, "n:v",
1022 				  longopts, NULL)) != -1) {
1023 		switch (opt) {
1024 		case 'n':
1025 			number = strtol(optarg, NULL, 10);
1026 			break;
1027 		case 'v':
1028 			verbose = 1;
1029 			break;
1030 		}
1031 	}
1032 
1033 	if (number >= 0)
1034 		return list_one_dev(number, true, verbose);
1035 
1036 	for (i = 0; i < MAX_NR_UBLK_DEVS; i++) {
1037 		int ret = list_one_dev(i, false, verbose);
1038 
1039 		if (ret == -EOPNOTSUPP)
1040 			return ret;
1041 	}
1042 
1043 	return 0;
1044 }
1045 
cmd_dev_list_usage(const char * cmd)1046 static void cmd_dev_list_usage(const char *cmd)
1047 {
1048 	printf("%s list [-n DEV_ID]\n", cmd);
1049 }
1050 
1051 #define const_ilog2(x) (63 - __builtin_clzll(x))
1052 
cmd_dev_get_features(int argc,char * argv[])1053 static int cmd_dev_get_features(int argc, char *argv[])
1054 {
1055 	struct ublksrv_dev_data data = {
1056 		.dev_id = -1,
1057 		.run_dir = UBLKSRV_PID_DIR,
1058 	};
1059 	struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
1060 	__u64 features = 0;
1061 	int ret;
1062 	static const char *feat_map[] = {
1063 		[const_ilog2(UBLK_F_SUPPORT_ZERO_COPY)] = "ZERO_COPY",
1064 		[const_ilog2(UBLK_F_URING_CMD_COMP_IN_TASK)] = "COMP_IN_TASK",
1065 		[const_ilog2(UBLK_F_NEED_GET_DATA)] = "GET_DATA",
1066 		[const_ilog2(UBLK_F_USER_RECOVERY)] = "USER_RECOVERY",
1067 		[const_ilog2(UBLK_F_USER_RECOVERY_REISSUE)] = "RECOVERY_REISSUE",
1068 		[const_ilog2(UBLK_F_UNPRIVILEGED_DEV)] = "UNPRIVILEGED_DEV",
1069 		[const_ilog2(UBLK_F_CMD_IOCTL_ENCODE)] = "CMD_IOCTL_ENCODE",
1070 		[const_ilog2(UBLK_F_USER_COPY)] = "USER_COPY",
1071 		[const_ilog2(UBLK_F_ZONED)] = "ZONED",
1072 		[const_ilog2(UBLK_F_USER_RECOVERY_FAIL_IO)] = "RECOVERY_FAIL_IO",
1073 	};
1074 
1075 	if (!dev) {
1076 		fprintf(stderr, "ublksrv_ctrl_init failed id\n");
1077 		return -EOPNOTSUPP;
1078 	}
1079 
1080 	ret = ublksrv_ctrl_get_features(dev, &features);
1081 	if (!ret) {
1082 		int i;
1083 
1084 		printf("ublk_drv features: 0x%llx\n", features);
1085 
1086 		for (i = 0; i < sizeof(features) * 8; i++) {
1087 			const char *feat;
1088 
1089 			if (!((1ULL << i)  & features))
1090 				continue;
1091 			if (i < sizeof(feat_map) / sizeof(feat_map[0]))
1092 				feat = feat_map[i];
1093 			else
1094 				feat = "unknown";
1095 			printf("\t%-20s: 0x%llx\n", feat, 1ULL << i);
1096 		}
1097 	}
1098 
1099 	return ret;
1100 }
1101 
cmd_dev_get_features_help(const char * cmd)1102 static void cmd_dev_get_features_help(const char *cmd)
1103 {
1104 	printf("%s features\n", cmd);
1105 }
1106 
__cmd_dev_user_recover(int number,bool verbose)1107 static int __cmd_dev_user_recover(int number, bool verbose)
1108 {
1109 	const struct ublksrv_tgt_type *tgt_type;
1110 	struct ublksrv_dev_data data = {
1111 		.dev_id = number,
1112 		.run_dir = UBLKSRV_PID_DIR,
1113 	};
1114 	struct ublksrv_ctrl_dev_info  dev_info;
1115 	struct ublksrv_ctrl_dev *dev;
1116 	struct ublksrv_tgt_base_json tgt_json = {0};
1117 	char *buf = NULL;
1118 	char pid_file[64];
1119 	int ret;
1120 	unsigned elapsed = 0;
1121 
1122 	dev = ublksrv_ctrl_init(&data);
1123 	if (!dev) {
1124 		fprintf(stderr, "ublksrv_ctrl_init failure dev %d\n", number);
1125 		return -EOPNOTSUPP;
1126 	}
1127 
1128 	ret = ublksrv_ctrl_get_info(dev);
1129 	if (ret < 0) {
1130 		fprintf(stderr, "can't get dev info from %d\n", number);
1131 		goto fail;
1132 	}
1133 
1134 	while (elapsed < 30000000) {
1135 		unsigned unit = 100000;
1136 		ret = ublksrv_ctrl_start_recovery(dev);
1137 		if (ret < 0 && ret != -EBUSY) {
1138 			fprintf(stderr, "can't start recovery for %d ret %d\n",
1139 					number, ret);
1140 			goto fail;
1141 		}
1142 		if (ret >= 0)
1143 			break;
1144 		usleep(unit);
1145 		elapsed += unit;
1146 	}
1147 
1148 	buf = ublksrv_tgt_get_dev_data(dev);
1149 	if (!buf) {
1150 		fprintf(stderr, "get dev %d data failed\n", number);
1151 		ret = -1;
1152 		goto fail;
1153 	}
1154 
1155 	ret = ublksrv_json_read_dev_info(buf, &dev_info);
1156 	if (ret < 0) {
1157 		fprintf(stderr, "can't read dev info for %d\n", number);
1158 		goto fail;
1159 	}
1160 
1161 	if (dev_info.dev_id != number) {
1162 		fprintf(stderr, "dev id doesn't match read %d for dev %d\n",
1163 				dev_info.dev_id, number);
1164 		goto fail;
1165 	}
1166 
1167 	ret = ublksrv_json_read_target_base_info(buf, &tgt_json);
1168 	if (ret < 0) {
1169 		fprintf(stderr, "can't read dev info for %d\n", number);
1170 		goto fail;
1171 	}
1172 
1173 	snprintf(pid_file, 64, "%s/%d.pid", data.run_dir, number);
1174 	ret = unlink(pid_file);
1175 	if (ret < 0) {
1176 		fprintf(stderr, "can't delete old pid_file for %d, error:%s\n",
1177 				number, strerror(errno));
1178 		goto fail;
1179 	}
1180 
1181 	tgt_type = ublksrv_find_tgt_type(tgt_json.name);
1182 	if (!tgt_type) {
1183 		fprintf(stderr, "can't find target type %s\n", tgt_json.name);
1184 		goto fail;
1185 	}
1186 
1187 	ublksrv_ctrl_prep_recovery(dev, tgt_json.name, tgt_type, buf);
1188 
1189 	ret = ublksrv_start_daemon(dev);
1190 	if (ret < 0) {
1191 		fprintf(stderr, "start daemon %d failed\n", number);
1192 		goto fail;
1193 	}
1194 
1195 	ret = ublksrv_ctrl_end_recovery(dev, ret);
1196 	if (ret < 0) {
1197 		fprintf(stderr, "end recovery for %d failed\n", number);
1198 		goto fail;
1199 	}
1200 
1201 	ret = ublksrv_ctrl_get_info(dev);
1202 	if (ret < 0) {
1203 		fprintf(stderr, "can't get dev info from %d\n", number);
1204 		goto fail;
1205 	}
1206 
1207 	if (verbose) {
1208 		free(buf);
1209 		buf = ublksrv_tgt_get_dev_data(dev);
1210 		ublksrv_ctrl_dump(dev, buf);
1211 	}
1212 
1213  fail:
1214 	free(buf);
1215 	ublksrv_ctrl_deinit(dev);
1216 	return ret;
1217 }
1218 
cmd_dev_user_recover(int argc,char * argv[])1219 static int cmd_dev_user_recover(int argc, char *argv[])
1220 {
1221 	static const struct option longopts[] = {
1222 		{ "number",		0,	NULL, 'n' },
1223 		{ "verbose",	0,	NULL, 'v' },
1224 		{ NULL }
1225 	};
1226 	int number = -1;
1227 	int opt;
1228 	bool verbose = false;
1229 
1230 	while ((opt = getopt_long(argc, argv, "n:v",
1231 				  longopts, NULL)) != -1) {
1232 		switch (opt) {
1233 		case 'n':
1234 			number = strtol(optarg, NULL, 10);
1235 			break;
1236 		case 'v':
1237 			verbose = true;
1238 			break;
1239 		}
1240 	}
1241 
1242 	return __cmd_dev_user_recover(number, verbose);
1243 }
1244 
cmd_dev_recover_usage(const char * cmd)1245 static void cmd_dev_recover_usage(const char *cmd)
1246 {
1247 	printf("%s recover [-n DEV_ID]\n", cmd);
1248 }
1249 
cmd_usage(const char * cmd)1250 static void cmd_usage(const char *cmd)
1251 {
1252 	cmd_dev_add_usage(cmd);
1253 	cmd_dev_del_usage(cmd);
1254 	cmd_dev_list_usage(cmd);
1255 	cmd_dev_recover_usage(cmd);
1256 	cmd_dev_get_features_help(cmd);
1257 
1258 	printf("%s -v [--version]\n", cmd);
1259 	printf("%s -h [--help]\n", cmd);
1260 }
1261 
main(int argc,char * argv[])1262 int main(int argc, char *argv[])
1263 {
1264 	const char *prog_name = "ublk";
1265 
1266 	char *cmd;
1267 	int ret;
1268 	char exe[PATH_MAX];
1269 
1270 	full_cmd = argv[0];
1271 	strncpy(exe, full_cmd, PATH_MAX - 1);
1272 
1273 	setvbuf(stdout, NULL, _IOLBF, 0);
1274 
1275 	cmd = pop_cmd(&argc, argv);
1276 	if (cmd == NULL) {
1277 		printf("%s: missing command\n", argv[0]);
1278 		cmd_usage(prog_name);
1279 		return EXIT_FAILURE;
1280 	}
1281 
1282 	if (!strcmp(cmd, "add"))
1283 		ret = cmd_dev_add(argc, argv);
1284 	else if (!strcmp(cmd, "del"))
1285 		ret = cmd_dev_del(argc, argv);
1286 	else if (!strcmp(cmd, "list"))
1287 		ret = cmd_list_dev_info(argc, argv);
1288 	else if (!strcmp(cmd, "recover"))
1289 		ret = cmd_dev_user_recover(argc, argv);
1290 	else if (!strcmp(cmd, "features"))
1291 		ret = cmd_dev_get_features(argc, argv);
1292 	else if (!strcmp(cmd, "help") || !strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
1293 		cmd_usage(prog_name);
1294 		ret = EXIT_SUCCESS;
1295 	} else if (!strcmp(cmd, "-v") || !strcmp(cmd, "--version")) {
1296 		fprintf(stdout, "%s\n", PACKAGE_STRING);
1297 		ret = EXIT_SUCCESS;
1298 	} else {
1299 		fprintf(stderr, "unknown command: %s\n", cmd);
1300 		cmd_usage(prog_name);
1301 		ret = EXIT_FAILURE;
1302 	}
1303 
1304 	ublk_ctrl_dbg(UBLK_DBG_CTRL_CMD, "cmd %s: result %d\n", cmd, ret);
1305 
1306 	return ret;
1307 }
1308