obex-client-tool.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. *
  4. * OBEX library with GLib integration
  5. *
  6. * Copyright (C) 2011 Intel Corporation. All rights reserved.
  7. *
  8. */
  9. #ifdef HAVE_CONFIG_H
  10. #include <config.h>
  11. #endif
  12. #define _GNU_SOURCE
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <fcntl.h>
  16. #include <sys/un.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <errno.h>
  21. #include <stdio.h>
  22. #include <readline/readline.h>
  23. #include <readline/history.h>
  24. #include "gobex/gobex.h"
  25. #include "btio/btio.h"
  26. static GMainLoop *main_loop = NULL;
  27. static GObex *obex = NULL;
  28. static gboolean option_packet = FALSE;
  29. static gboolean option_bluetooth = FALSE;
  30. static char *option_source = NULL;
  31. static char *option_dest = NULL;
  32. static int option_channel = -1;
  33. static int option_imtu = -1;
  34. static int option_omtu = -1;
  35. static void sig_term(int sig)
  36. {
  37. g_print("Terminating due to signal %d\n", sig);
  38. g_main_loop_quit(main_loop);
  39. }
  40. static GOptionEntry options[] = {
  41. { "unix", 'u', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
  42. &option_bluetooth, "Use a UNIX socket" },
  43. { "bluetooth", 'b', 0, G_OPTION_ARG_NONE,
  44. &option_bluetooth, "Use Bluetooth" },
  45. { "source", 's', 0, G_OPTION_ARG_STRING,
  46. &option_source, "Bluetooth adapter address",
  47. "00:..." },
  48. { "destination", 'd', 0, G_OPTION_ARG_STRING,
  49. &option_dest, "Remote bluetooth address",
  50. "00:..." },
  51. { "channel", 'c', 0, G_OPTION_ARG_INT,
  52. &option_channel, "Transport channel", "CHANNEL" },
  53. { "packet", 'p', 0, G_OPTION_ARG_NONE,
  54. &option_packet, "Packet based transport" },
  55. { "stream", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,
  56. &option_packet, "Stream based transport" },
  57. { "input-mtu", 'i', 0, G_OPTION_ARG_INT,
  58. &option_imtu, "Transport input MTU", "MTU" },
  59. { "output-mtu", 'o', 0, G_OPTION_ARG_INT,
  60. &option_omtu, "Transport output MTU", "MTU" },
  61. { NULL },
  62. };
  63. static void conn_complete(GObex *obex, GError *err, GObexPacket *rsp,
  64. gpointer user_data)
  65. {
  66. if (err != NULL)
  67. g_print("Connect failed: %s\n", err->message);
  68. else
  69. g_print("Connect succeeded\n");
  70. }
  71. static void cmd_connect(int argc, char **argv)
  72. {
  73. g_obex_connect(obex, conn_complete, NULL, NULL, G_OBEX_HDR_INVALID);
  74. }
  75. struct transfer_data {
  76. int fd;
  77. };
  78. static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
  79. {
  80. struct transfer_data *data = user_data;
  81. if (err != NULL)
  82. g_printerr("failed: %s\n", err->message);
  83. else
  84. g_print("transfer succeeded\n");
  85. close(data->fd);
  86. g_free(data);
  87. }
  88. static gssize put_data_cb(void *buf, gsize len, gpointer user_data)
  89. {
  90. struct transfer_data *data = user_data;
  91. return read(data->fd, buf, len);
  92. }
  93. static void cmd_put(int argc, char **argv)
  94. {
  95. struct transfer_data *data;
  96. GError *err = NULL;
  97. int fd;
  98. if (argc < 2) {
  99. g_printerr("Filename required\n");
  100. return;
  101. }
  102. fd = open(argv[1], O_RDONLY | O_NOCTTY, 0);
  103. if (fd < 0) {
  104. g_printerr("open: %s\n", strerror(errno));
  105. return;
  106. }
  107. data = g_new0(struct transfer_data, 1);
  108. data->fd = fd;
  109. g_obex_put_req(obex, put_data_cb, transfer_complete, data, &err,
  110. G_OBEX_HDR_NAME, argv[1],
  111. G_OBEX_HDR_INVALID);
  112. if (err != NULL) {
  113. g_printerr("put failed: %s\n", err->message);
  114. g_error_free(err);
  115. close(data->fd);
  116. g_free(data);
  117. }
  118. }
  119. static gboolean get_data_cb(const void *buf, gsize len, gpointer user_data)
  120. {
  121. struct transfer_data *data = user_data;
  122. if (write(data->fd, buf, len) < 0) {
  123. g_printerr("write: %s\n", strerror(errno));
  124. return FALSE;
  125. }
  126. return TRUE;
  127. }
  128. static void cmd_get(int argc, char **argv)
  129. {
  130. struct transfer_data *data;
  131. GError *err = NULL;
  132. int fd;
  133. if (argc < 2) {
  134. g_printerr("Filename required\n");
  135. return;
  136. }
  137. fd = open(argv[1], O_WRONLY | O_CREAT | O_NOCTTY, 0600);
  138. if (fd < 0) {
  139. g_printerr("open: %s\n", strerror(errno));
  140. return;
  141. }
  142. data = g_new0(struct transfer_data, 1);
  143. data->fd = fd;
  144. g_obex_get_req(obex, get_data_cb, transfer_complete, data, &err,
  145. G_OBEX_HDR_NAME, argv[1],
  146. G_OBEX_HDR_INVALID);
  147. if (err != NULL) {
  148. g_printerr("get failed: %s\n", err->message);
  149. g_error_free(err);
  150. close(data->fd);
  151. g_free(data);
  152. }
  153. }
  154. static void cmd_help(int argc, char **argv);
  155. static void cmd_exit(int argc, char **argv)
  156. {
  157. g_main_loop_quit(main_loop);
  158. }
  159. static struct {
  160. const char *cmd;
  161. void (*func)(int argc, char **argv);
  162. const char *params;
  163. const char *desc;
  164. } commands[] = {
  165. { "help", cmd_help, "", "Show this help"},
  166. { "exit", cmd_exit, "", "Exit application" },
  167. { "quit", cmd_exit, "", "Exit application" },
  168. { "connect", cmd_connect, "[target]", "OBEX Connect" },
  169. { "put", cmd_put, "<file>", "Send a file" },
  170. { "get", cmd_get, "<file>", "Receive a file" },
  171. { NULL },
  172. };
  173. static void cmd_help(int argc, char **argv)
  174. {
  175. int i;
  176. for (i = 0; commands[i].cmd; i++)
  177. printf("%-15s %-30s %s\n", commands[i].cmd,
  178. commands[i].params, commands[i].desc);
  179. }
  180. static void parse_line(char *line_read)
  181. {
  182. char **argvp;
  183. int argcp;
  184. int i;
  185. if (line_read == NULL) {
  186. g_print("\n");
  187. g_main_loop_quit(main_loop);
  188. return;
  189. }
  190. line_read = g_strstrip(line_read);
  191. if (*line_read == '\0') {
  192. free(line_read);
  193. return;
  194. }
  195. if (history_search(line_read, -1))
  196. add_history(line_read);
  197. g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
  198. free(line_read);
  199. for (i = 0; commands[i].cmd; i++)
  200. if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
  201. break;
  202. if (commands[i].cmd)
  203. commands[i].func(argcp, argvp);
  204. else
  205. g_print("%s: command not found\n", argvp[0]);
  206. g_strfreev(argvp);
  207. }
  208. static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
  209. gpointer user_data)
  210. {
  211. if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
  212. g_main_loop_quit(main_loop);
  213. return FALSE;
  214. }
  215. rl_callback_read_char();
  216. return TRUE;
  217. }
  218. static void disconn_func(GObex *obex, GError *err, gpointer user_data)
  219. {
  220. g_printerr("Disconnected: %s\n", err ? err->message : "(no error)");
  221. g_main_loop_quit(main_loop);
  222. }
  223. static void transport_connect(GIOChannel *io, GObexTransportType transport)
  224. {
  225. GIOChannel *input;
  226. GIOCondition events;
  227. g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
  228. g_io_channel_set_close_on_unref(io, TRUE);
  229. obex = g_obex_new(io, transport, option_imtu, option_omtu);
  230. g_obex_set_disconnect_function(obex, disconn_func, NULL);
  231. input = g_io_channel_unix_new(STDIN_FILENO);
  232. g_io_channel_set_close_on_unref(input, TRUE);
  233. events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
  234. g_io_add_watch(input, events, prompt_read, NULL);
  235. g_io_channel_unref(input);
  236. rl_callback_handler_install("client> ", parse_line);
  237. }
  238. static GIOChannel *unix_connect(GObexTransportType transport)
  239. {
  240. GIOChannel *io;
  241. struct sockaddr_un addr = {
  242. AF_UNIX, "\0/gobex/server"
  243. };
  244. int sk, err, sock_type;
  245. if (option_packet)
  246. sock_type = SOCK_SEQPACKET;
  247. else
  248. sock_type = SOCK_STREAM;
  249. sk = socket(PF_LOCAL, sock_type, 0);
  250. if (sk < 0) {
  251. err = errno;
  252. g_printerr("Can't create unix socket: %s (%d)\n",
  253. strerror(err), err);
  254. return NULL;
  255. }
  256. if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  257. err = errno;
  258. g_printerr("connect: %s (%d)\n", strerror(err), err);
  259. return NULL;
  260. }
  261. io = g_io_channel_unix_new(sk);
  262. g_print("Unix socket created: %d\n", sk);
  263. transport_connect(io, transport);
  264. return io;
  265. }
  266. static void conn_callback(GIOChannel *io, GError *err, gpointer user_data)
  267. {
  268. GObexTransportType transport = GPOINTER_TO_UINT(user_data);
  269. if (err != NULL) {
  270. g_printerr("%s\n", err->message);
  271. return;
  272. }
  273. g_print("Bluetooth socket connected\n");
  274. transport_connect(io, transport);
  275. }
  276. static GIOChannel *l2cap_connect(GObexTransportType transport, GError **err)
  277. {
  278. if (option_source)
  279. return bt_io_connect(conn_callback,
  280. GUINT_TO_POINTER(transport),
  281. NULL, err,
  282. BT_IO_OPT_SOURCE, option_source,
  283. BT_IO_OPT_DEST, option_dest,
  284. BT_IO_OPT_PSM, option_channel,
  285. BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
  286. BT_IO_OPT_OMTU, option_omtu,
  287. BT_IO_OPT_IMTU, option_imtu,
  288. BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
  289. BT_IO_OPT_INVALID);
  290. return bt_io_connect(conn_callback,
  291. GUINT_TO_POINTER(transport),
  292. NULL, err,
  293. BT_IO_OPT_DEST, option_dest,
  294. BT_IO_OPT_PSM, option_channel,
  295. BT_IO_OPT_MODE, BT_IO_MODE_ERTM,
  296. BT_IO_OPT_OMTU, option_omtu,
  297. BT_IO_OPT_IMTU, option_imtu,
  298. BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
  299. BT_IO_OPT_INVALID);
  300. }
  301. static GIOChannel *rfcomm_connect(GObexTransportType transport, GError **err)
  302. {
  303. if (option_source)
  304. return bt_io_connect(conn_callback,
  305. GUINT_TO_POINTER(transport),
  306. NULL, err,
  307. BT_IO_OPT_SOURCE, option_source,
  308. BT_IO_OPT_DEST, option_dest,
  309. BT_IO_OPT_CHANNEL, option_channel,
  310. BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
  311. BT_IO_OPT_INVALID);
  312. return bt_io_connect(conn_callback,
  313. GUINT_TO_POINTER(transport),
  314. NULL, err,
  315. BT_IO_OPT_DEST, option_dest,
  316. BT_IO_OPT_CHANNEL, option_channel,
  317. BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
  318. BT_IO_OPT_INVALID);
  319. }
  320. static GIOChannel *bluetooth_connect(GObexTransportType transport)
  321. {
  322. GIOChannel *io;
  323. GError *err = NULL;
  324. if (option_dest == NULL || option_channel < 0)
  325. return NULL;
  326. if (option_channel > 31)
  327. io = l2cap_connect(transport, &err);
  328. else
  329. io = rfcomm_connect(transport, &err);
  330. if (io != NULL)
  331. return io;
  332. g_printerr("%s\n", err->message);
  333. g_error_free(err);
  334. return NULL;
  335. }
  336. int main(int argc, char *argv[])
  337. {
  338. GOptionContext *context;
  339. GError *err = NULL;
  340. struct sigaction sa;
  341. GIOChannel *io;
  342. GObexTransportType transport;
  343. context = g_option_context_new(NULL);
  344. g_option_context_add_main_entries(context, options, NULL);
  345. g_option_context_parse(context, &argc, &argv, &err);
  346. if (err != NULL) {
  347. g_printerr("%s\n", err->message);
  348. g_error_free(err);
  349. g_option_context_free(context);
  350. exit(EXIT_FAILURE);
  351. }
  352. if (option_packet)
  353. transport = G_OBEX_TRANSPORT_PACKET;
  354. else
  355. transport = G_OBEX_TRANSPORT_STREAM;
  356. if (option_bluetooth)
  357. io = bluetooth_connect(transport);
  358. else
  359. io = unix_connect(transport);
  360. if (io == NULL) {
  361. g_option_context_free(context);
  362. exit(EXIT_FAILURE);
  363. }
  364. memset(&sa, 0, sizeof(sa));
  365. sa.sa_handler = sig_term;
  366. sigaction(SIGINT, &sa, NULL);
  367. sigaction(SIGTERM, &sa, NULL);
  368. main_loop = g_main_loop_new(NULL, FALSE);
  369. g_main_loop_run(main_loop);
  370. rl_callback_handler_remove();
  371. clear_history();
  372. g_obex_unref(obex);
  373. g_option_context_free(context);
  374. g_main_loop_unref(main_loop);
  375. exit(EXIT_SUCCESS);
  376. }