gregory-nutt commented on code in PR #10602:
URL: https://github.com/apache/nuttx/pull/10602#discussion_r1324554592


##########
libs/libc/stdio/lib_fopencookie.c:
##########
@@ -0,0 +1,229 @@
+/****************************************************************************
+ * libs/libc/stdio/lib_fopencookie.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "libc.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct cookie_s
+{
+  void *cookie;                    /* pointer to the caller's cookie struct */
+  cookie_io_functions_t cookie_io; /* programmer-defined hook functions */
+  int cookie_fd;                   /* file descriptor */
+};
+
+static ssize_t freadcookie(FAR struct file *filep, FAR char *buf,
+                           size_t nbytes);
+static ssize_t fwritecookie(FAR struct file *filep, FAR const char *buf,
+                            size_t nbytes);
+static off_t fseekcookie(FAR struct file *filep, off_t offset, int whence);
+static int fclosecookie(FAR struct file *filep);
+
+/****************************************************************************
+  * Private Data
+ ****************************************************************************/
+
+static const struct file_operations cookie_fops =
+{
+  NULL,         /* open */
+  fclosecookie, /* close */
+  freadcookie,  /* read */
+  fwritecookie, /* write */
+  fseekcookie,  /* seek */
+  NULL,         /* ioctl */
+  NULL,         /* mmap */
+  NULL,         /* truncate */
+  NULL,         /* poll */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static ssize_t freadcookie(FAR struct file *filep, FAR char *buf,
+                           size_t nbytes)
+{
+  struct cookie_s *cookie = (struct cookie_s *)filep->f_priv;
+  int ret;
+
+  DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.read != NULL);
+
+  ret = cookie->cookie_io.read(cookie->cookie, buf, nbytes);
+  return ret;
+}
+
+static ssize_t fwritecookie(FAR struct file *filep, FAR const char *buf,
+                            size_t nbytes)
+{
+  struct cookie_s *cookie = (struct cookie_s *)filep->f_priv;
+  int ret;
+
+  DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.write != NULL);
+
+  ret = cookie->cookie_io.write(cookie->cookie, (FAR const char *)buf,
+                               nbytes);
+  return ret;
+}
+
+static off_t fseekcookie(FAR struct file *filep, off_t offset, int whence)
+{
+  struct cookie_s *cookie = (struct cookie_s *)filep->f_priv;
+  int ret;
+
+  DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.seek != NULL);
+
+  ret = cookie->cookie_io.seek(cookie->cookie, &offset, whence);
+  return ret;
+}
+
+static int fclosecookie(FAR struct file *filep)
+{
+  struct cookie_s *cookie = (struct cookie_s *)filep->f_priv;
+  int ret;
+
+  DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.close != NULL);
+
+  ret = cookie->cookie_io.close(cookie->cookie);
+
+  free(filep->f_priv);
+  free(filep->f_inode);
+  close(cookie->cookie_fd);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fopencookie
+ ****************************************************************************/
+
+FAR FILE *fopencookie(void *restrict cookie, const char *restrict mode,
+                      cookie_io_functions_t io_funcs)
+{
+  FAR FILE *filestructp = NULL;
+  FAR struct file filep;
+  FAR struct inode *inode;
+  FAR struct cookie_s *cookie_priv;
+  int oflags;
+  int ret;
+  int fd;
+
+  /* Map the open mode string to open flags */
+
+  oflags = lib_mode2oflags(mode);
+  if (oflags < 0)
+    {
+      return NULL;
+    }
+
+  /* Allocate necessary structures. */
+
+  inode = lib_zalloc(sizeof(struct inode));
+  if (inode == NULL)
+    {
+      set_errno(-ENOMEM);
+      goto fopencookie_return;
+    }
+
+  cookie_priv = lib_zalloc(sizeof(struct cookie_s));
+  if (cookie_priv == NULL)
+    {
+      free(inode);
+      set_errno(-ENOMEM);
+      goto fopencookie_return;
+    }
+
+  memset(&filep, 0, sizeof(filep));
+
+  /* Fill cookie_priv structure. We need to add cookie provided by user
+   * and functions providd by user.
+   */
+
+  cookie_priv->cookie = cookie;
+  cookie_priv->cookie_io.read  = io_funcs.read;
+  cookie_priv->cookie_io.write = io_funcs.write;
+  cookie_priv->cookie_io.seek  = io_funcs.seek;
+  cookie_priv->cookie_io.close = io_funcs.close;
+
+  /* Add file operations */
+
+  inode->u.i_ops = &cookie_fops;
+
+  filep.f_oflags = oflags;
+  filep.f_inode = inode;
+  filep.f_priv = cookie_priv;
+
+  /* And get file descriptor. */
+
+  fd = file_allocate_from_tcb(nxsched_self(), filep.f_inode, filep.f_oflags,

Review Comment:
   > Ok, you implementation reuse file_operation_s infrastructure, but it 
mayn't work in protected/kernel mode, since fclosecookie... is part of kernel, 
but call the callback provided by userspace directly. I would suggest that you 
have to add these callback to file_struct and call them from libc/stdio 
directly.
   > 
   > @patacongo what are you thinking?
   
   _[I have not looked at this particular PR, but I can give my usual response 
when callbacks from the OS are suggested]_
   
   Direct callbacks from the OS into user code have always been forbidden.  I 
don't think we should permit that now.  Some justifications:
   
   - This is not the POSIX way to do things.  There is no way to do such things 
under any POSIX OS.  We have always been concerned about POSIX compliant and 
portability.  Linux code is often ported to NuttX. NuttX code should be 
runnable on any any POSIX OS as well (with the appropriate drivers).  To 
accomplish these goals, callbacks must not be used.
   
   - The callback would probably be running on an kernel stack and not the user 
stack.  In that case, TLS data on the stack would not be accessible.  The 
callback were asynchronous with the tasking.  A callback is not a context 
switch; the interrupt user task is still in place and the OS thinks that is 
that task that is running.  So any OS interactions in this bastardized state 
could likely be fatal.
   
   - The callback would run at whatever the priority of the calling logic is 
running at.  This will break real time behavior which must be strict 
priority-based scheduling.
   
   There are several differ build modes for NuttX:  The FLAT build, the 
PROTECTED build for better security, and the KERNEL build for full support of 
processes much like Linux.  In the PROTECTED and KERNEL builds, there are more 
significant technical issues.
   
   There is some documentation of at least the PROTECTED build here:  
https://cwiki.apache.org/confluence/display/NUTTX/NuttX+Protected+Build
   
   - In the PROTECTED and KERNEL build modes, the kernel runs in supervisor 
mode in a supervisor address space while all user applications run in 
non-privileged user mode in a user address space.  In the PROTECTED mode, you 
_could_ call from the kernel supervisor code into user code but this would, 
however, be a gaping security hole and, hence, must never be done.  Potentially 
hostile user-supplied code must never run in supervisor mode in this build.
   
   - The KERNEL build differs in that each _process_ has its own address 
environment and, in this build, the system will crash if such a thing is 
attempted.  That is because the notification arrives asynchronously with 
tasking; you never know which process is running and you would call into the 
wrong address space.  This could be fixed be changing the address environment 
prior to each callback, that would have big performance and complexity issues.  
It is better to do things in the POSIX way.
   
   So even if you use only the FLAT build, direct callbacks from the OS into 
the user code must be prohibited.  What is the POSIX way of doing an 
aynchronous notification?  Signals.  In a POSIX OS, you should use standard 
POSIX signaling and avoid even thinking about callbacks.
   
   Drivers under drivers/ that do asynchronous notifications use signals for 
this purpose.  Consider drivers/input/button_upper.c.  This is a simple button 
driver that sends a notification to a user task when a button is pressed or 
release:
   
   - Look at the function btn_ioctl().  The BTNIOC_REGISTER command is used to 
register a notification.  The process ID of the user thread is provided and 
retained for the notification.
   
   - When the button press event occurs, the notification signal is sent in 
btn_sample().
   
   - A signal handler in the user thread that registered for the notification 
can then run, in the correct task context, in user mode, and at the correct 
priority assigned to the task.
   
   This is 100% POSIX and works in all build modes.  It could even be 
implemented under any other POSIX OS such as Linux.
   
   Signals are only one option.  Below I mention usrsock as a better model to 
follow.  In that case, I believe that it uses a character driver in kernel 
space to interact with the user-space socket implementationi.
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to