summaryrefslogtreecommitdiff
path: root/apps/examples/cc3000/telnetd_daemon.c
blob: 2237741908e03f11363d046e9ebf1369a15caca8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/****************************************************************************
 * apps/examples/cc3000/telnetd_daemon.c
 *
 *   Copyright (C) 2012 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name Gregory Nutt nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <nuttx/wireless/cc3000/include/sys/socket.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sched.h>
#include <errno.h>
#include <debug.h>
#include <netinet/in.h>

#include <apps/netutils/telnetd.h>
#include <apps/netutils/netlib.h>

#include "telnetd.h"

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

/****************************************************************************
 * Public Data
 ****************************************************************************/

/* This structure is used to passed information to telnet daemon when it
 * started.
 */

struct telnetd_common_s g_telnetdcommon;

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: telnetd_daemon
 *
 * Description:
 *   This function is the Telnet daemon.  It does not return (unless an
 *   error occurs).
 *
 * Parameters:
 *   Standard task start up arguments.
 *
 * Return:
 *   Does not return unless an error occurs.
 *
 ****************************************************************************/

static int telnetd_daemon(int argc, char *argv[])
{
  FAR struct telnetd_s *daemon;
  struct sockaddr_in myaddr;
#ifdef CONFIG_NET_SOLINGER
  struct linger ling;
#endif
  socklen_t addrlen;
  FAR char *devpath;
  pid_t pid;
  int listensd;
  int acceptsd;
  int drvrfd;
#ifdef CONFIG_NET_HAVE_REUSEADDR
  int optval;
#endif

  /* Get daemon startup info */

  daemon = g_telnetdcommon.daemon;
  g_telnetdcommon.daemon = NULL;
  sem_post(&g_telnetdcommon.startsem);
  DEBUGASSERT(daemon != NULL);

  /* Create a new TCP socket to use to listen for connections */

  listensd = socket(PF_INET, SOCK_STREAM, 0);
  if (listensd < 0)
    {
      int errval = errno;
      ndbg("socket failure: %d\n", errval);
      return -errval;
    }

  /* Set socket to reuse address */

#ifdef CONFIG_NET_HAVE_REUSEADDR
  optval = 1;
  if (setsockopt(listensd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(int)) < 0)
    {
      ndbg("setsockopt SO_REUSEADDR failure: %d\n", errno);
      goto errout_with_socket;
    }
#endif

  /* Bind the socket to a local address */

  myaddr.sin_family      = AF_INET;
  myaddr.sin_port        = daemon->port;
  myaddr.sin_addr.s_addr = INADDR_ANY;

  if (bind(listensd, (struct sockaddr*)&myaddr, sizeof(struct sockaddr_in)) < 0)
    {
      ndbg("bind failure: %d\n", errno);
      goto errout_with_socket;
    }

  /* Listen for connections on the bound TCP socket */

  if (listen(listensd, 5) < 0)
    {
      ndbg("listen failure %d\n", errno);
      goto errout_with_socket;
    }

  /* Now go silent.  Only the lldbg family of debug functions should
   * be used after this point because these do not depend on stdout
   * being available.
   */

#ifndef CONFIG_DEBUG
  close(0);
  close(1);
  close(2);
#endif

  /* Begin accepting connections */

  for (;;)
    {
      nllvdbg("Accepting connections on port %d\n", ntohs(daemon->port));

      addrlen = sizeof(struct sockaddr_in);
      acceptsd = accept(listensd, (struct sockaddr*)&myaddr, &addrlen);
      if (acceptsd < 0)
        {
          nlldbg("accept failed: %d\n", errno);
          goto errout_with_socket;
        }

      /* Configure to "linger" until all data is sent when the socket is closed */

#ifdef CONFIG_NET_SOLINGER
      ling.l_onoff  = 1;
      ling.l_linger = 30;     /* timeout is seconds */
      if (setsockopt(acceptsd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger)) < 0)
        {
          nlldbg("setsockopt failed: %d\n", errno);
          goto errout_with_acceptsd;
        }
#endif

      /* Create a character device to "wrap" the accepted socket descriptor */

      nllvdbg("Creating the telnet driver\n");
      devpath = telnetd_driver(acceptsd, daemon);
      if (devpath < 0)
        {
          nlldbg("telnetd_driver failed\n");
          goto errout_with_acceptsd;
        }

      /* Open the driver */

      nllvdbg("Opening the telnet driver\n");
      drvrfd = open(devpath, O_RDWR);
      if (drvrfd < 0)
        {
          nlldbg("Failed to open %s: %d\n", devpath, errno);
          goto errout_with_acceptsd;
        }

      /* We can now free the driver string */

      free(devpath);

      /* Use this driver as stdin, stdout, and stderror */

      (void)dup2(drvrfd, 0);
      (void)dup2(drvrfd, 1);
      (void)dup2(drvrfd, 2);

      /* And we can close our original driver fd */

      if (drvrfd > 2)
        {
          close(drvrfd);
        }

      /* Create a task to handle the connection.  The created task
       * will inherit the new stdin, stdout, and stderr.
       */

      nllvdbg("Starting the telnet session\n");
      pid = task_create("Telnet session", daemon->priority, daemon->stacksize,
                         daemon->entry, NULL);
      if (pid < 0)
        {
          nlldbg("Failed start the telnet session: %d\n", errno);
          goto errout_with_acceptsd;
        }

      /* Forget about the connection. */

      close(0);
      close(1);
      close(2);
    }

errout_with_acceptsd:
  close(acceptsd);

errout_with_socket:
  close(listensd);
  free(daemon);
  return 1;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: telnetd_start
 *
 * Description:
 *   Start the telnet daemon.
 *
 * Parameters:
 *   config    A pointer to a configuration structure that characterizes the
 *             telnet daemon.  This configuration structure may be defined
 *             on the caller's stack because it is not retained by the
 *             daemon.
 *
 * Return:
 *   The process ID (pid) of the new telnet daemon is returned on
 *   success; A negated errno is returned if the daemon was not successfully
 *   started.
 *
 ****************************************************************************/

int telnetd_start(FAR struct telnetd_config_s *config)
{
  FAR struct telnetd_s *daemon;
  pid_t pid;
  int ret;

  /* Allocate a state structure for the new daemon */

  daemon = (FAR struct telnetd_s *)malloc(sizeof(struct telnetd_s));
  if (!daemon)
    {
      return -ENOMEM;
    }

  /* Initialize the daemon structure */

  daemon->port      = config->d_port;
  daemon->priority  = config->t_priority;
  daemon->stacksize = config->t_stacksize;
  daemon->entry     = config->t_entry;

  /* Initialize the common structure if this is the first daemon */

  if (g_telnetdcommon.ndaemons < 1)
    {
      sem_init(&g_telnetdcommon.startsem, 0, 0);
      sem_init(&g_telnetdcommon.exclsem, 0, 1);
      g_telnetdcommon.minor = 0;
    }

  /* Then start the new daemon */

  g_telnetdcommon.daemon = daemon;
  pid = task_create("Telnet daemon", config->d_priority, config->d_stacksize,
                    telnetd_daemon, NULL);
  if (pid < 0)
    {
      int errval = errno;
      free(daemon);
      ndbg("Failed to start the telnet daemon: %d\n", errval);
      return -errval;
    }

  /* Then wait for the daemon to start and complete the handshake */

  do
    {
      ret = sem_wait(&g_telnetdcommon.startsem);

      /* The only expected error condition is for sem_wait to be awakened by
       * a receipt of a signal.
       */

      if (ret < 0)
        {
          DEBUGASSERT(errno == -EINTR);
        }
    }
  while (ret < 0);

  /* Return success */

  return pid;
}