Example #1
0
/******************************************************************************
** Name: MEsmdestroy()	- Destroy a shared memory segment
**
** Description:
**	Remove the shared memory segment specified by shared memory identifier
**	"key" from the system and destroy any system data structure associated
**	with it.
**
**	Note: The shared memory pages are not necessarily removed from
**	processes which have the segment mapped.  It is up to the clients to
**	detach the segment via MEfree_pages prior to destroying it.
**
**	Protection Note: The caller of this routine must have protection to
**	destroy the shared memory segment.  Protections are enforced by the
**	underlying Operating System.  In general, this routine can only be
**	guaranteed to work when executed by a user running with the same
**	effective privledges as the user who created the shared memory segment.
**
** Inputs:
**	user_key			identifier which was previosly
**				      used in a successful MEget_pages() call
**				      (not necessarily a call in this process)
**
** Outputs:
**	err_code 		System specific error information.
**
**	Returns:
**	    OK
**	    ME_NO_PERM		    No permission to destroy shared memory
**				            segment.
**	    ME_NO_SUCH_SEGMENT	indicated shared memory segment does not
**				            exist.
**	    ME_NO_SHARED	    No shared memory in this CL.
**	    ME_BAD_ADVICE	    call was made during ME_USER_ALLOC
**
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
******************************************************************************/
STATUS
MEsmdestroy(char        *user_key,
            CL_ERR_DESC *err_code)
{
	LOCATION        location;
	LOCATION        temp_loc;
	char            loc_buf[MAX_LOC + 1];
	STATUS          ret_val = OK;

	CLEAR_ERR(err_code);

	/*
	 * Get location of ME files for shared memory segments.
	 */

# ifdef MCT
	gen_Psem(&NM_loc_sem);
# endif /* MCT */
	ret_val = NMloc(FILES,
	                PATH,
	                (char *) NULL,
	                &temp_loc);
	if (!ret_val)
	{
		LOcopy(&temp_loc,
		       loc_buf,
		       &location);
		LOfaddpath(&location,
		           ME_SHMEM_DIR,
		           &location);
		LOfstfile(user_key,
		          &location);

		if (LOexist(&location) != OK)
			ret_val = FAIL;
		else {
			ret_val = LOdelete(&location);
			switch (ret_val) {
			case OK:
			case ME_NO_PERM:
				break;;

			default:
				ret_val = ME_NO_SUCH_SEGMENT;
				break;;
			}
		}
	}
# ifdef MCT
	gen_Vsem(&NM_loc_sem);
# endif /* MCT */
	return (ret_val);
}
Example #2
0
/******************************************************************************
**
** Name: DIalloc - Allocates a page to a direct access file.
**
** Description:
**      The DIalloc routine is used to add pages to a direct
**      access file.  This routine can add more than one page
**      at a time by accepting a count of the number of pages to add.
**
**      The end of file and allocated are not updated on disk until a DIflush
**      call is issued.  This insures that pages are not considered valid
**      until after they are formatted.  The allocation can be ignored if
**      the file is closed or the system crashes before the DIflush.
**
** Inputs:
**      f                Pointer to the DI file
**                       context needed to do I/O.
**      n                The number of pages to allocate.
**
** Outputs:
**      page             Pointer to variable used to
**                       return the page number of the
**                       first page allocated.
**      err_code         Pointer to a variable used
**                       to return operating system
**                       errors.
**    Returns:
**        OK
**        DI_BADEXTEND      Can't allocate disk space
**        DI_BADFILE        Bad file context.
**        DI_EXCEED_LIMIT   Too many open files.
**    Exceptions:
**        none
**
** Side Effects:
**        none
** History:
**	09-feb-1996 (canor01)
**	    Get exclusive semaphore on DI_IO before updating it in DI_sense
**	08-dec-1997 (canor01)
**	    Implement LRU for open files (initial copy from Unix).
**	28-jan-1998 (canor01)
**	    Optimize LRU--only call DIlru_open if file has been closed.
**      06-aug-1999 (mcgem01)
**          Replace nat and longnat with i4.
**	13-Nov-2009 (kschendel) SIR 122757
**	    Make io-sem a SYNCH.
**
******************************************************************************/
STATUS
DIalloc(DI_IO      *f,
        i4         n,
        i4         *page,
        CL_SYS_ERR *err_code)
{
    STATUS status = OK;

    CLEAR_ERR(err_code);

    /*
     * Check file control block pointer, return if bad.
     */

    if (f->io_type != DI_IO_ASCII_ID)
    	return (DI_BADFILE);

    CS_synch_lock( &f->io_sem );

    /* get file descriptor for this file */
    do
    {
	if ( f->io_nt_fh == INVALID_HANDLE_VALUE )
        status = DIlru_open( f, FALSE, err_code);
        if ( status != OK )
            break;
 
        status = DI_sense( f, page, err_code );
 
	if ( status != OK )
	    break;

	*page = (i4) (f->io_system_eof + 1);
	f->io_system_eof += n;
 
    } while (FALSE);
 
    CS_synch_unlock( &f->io_sem );
 
    return( status );

}
Example #3
0
/*****************************************************************************
** Name: MEshared_free()	- Free shared memory
**
** Description:
**	Free a region of shared memory and return new region for potential
**	futher freeing of the break region.
**
**	If there is attached shared memory in the region being
**	freed then those pages should only be marked free if the whole
**	segment can be detached.
**
**	There is a small table of attached segments which is scanned.
**
** Inputs:
** addr				address of region
**	pages				number of pages to check
**
** Outputs:
**	addr				new address of region
**	pages				new number of pages
**	err_code			CL_ERR_DESC
**
**	Returns:
**	    OK
**	    ME_NOT_ALLOCATED
**
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	07-apr-1997 (canor01)
**	    When the shared memory is attached to existing memory
**	    the file handle will be null, so don't try to close it.
**	10-apr-1997 (canor01)
**	    If the address of the memory to be freed does not fall
**	    within the shared memory range, return ME_NOT_ALLOCATED.
**
*****************************************************************************/
STATUS
MEshared_free(PTR         *addr,
              SIZE_TYPE          *pages,
              CL_ERR_DESC *err_code)
{
	STATUS			status = OK;
	register ME_SEG_INFO	*seginf;
	char			*lower, *upper, *last;
	SIZE_TYPE		off, len;
	QUEUE			*next_queue;

	CLEAR_ERR(err_code);

	lower = (char *) *addr;
	upper = lower + ME_MPAGESIZE * *pages;
	last = NULL;

	gen_Psem(&ME_segpool_sem);
	seginf = ME_find_seg(lower, upper, &ME_segpool);
	if ( seginf == NULL )
	{
	    /* memory address was not within shared memory range */
	    gen_Vsem(&ME_segpool_sem);
	    return( ME_NOT_ALLOCATED );
	}
	for ( ;
	     seginf;
	     seginf = ME_find_seg(lower,
	                          upper,
	                          next_queue)) {

		next_queue = &seginf->q;

		if (last && last != seginf->eaddr) {
			status = ME_NOT_ALLOCATED;
			break;
		}

		last = seginf->addr;
		off  = 0;
		len  = seginf->npages;

		if (lower > seginf->addr) {
			off = (lower - seginf->addr) / ME_MPAGESIZE;
			len -= off;
		}

		if (upper < seginf->eaddr) {
			len -= (seginf->eaddr - upper) / ME_MPAGESIZE;
		}

		if (MEalloctst(seginf->allocvec,
		               (i4)off,
		               (i4)len,
		               TRUE)) {
			status = ME_NOT_ALLOCATED;
			break;
		}

		MEclearpg(seginf->allocvec, (i4)off, (i4)len);

		if (!MEalloctst(seginf->allocvec,
		                0,
		                seginf->npages,
		                FALSE)) {
			/* detach segment */
			/*
			 * WARNING::: if the address is NOT the BASE of a
			 * shared memory segment obtained from MapViewOfFile,
			 * we're either gonna fail or puke. Good luck!
			 */

			status = UnmapViewOfFile(seginf->addr);

			if (status == FALSE) {
				status = GetLastError();
				SETCLOS2ERR(err_code, status, ER_mmap);
				gen_Vsem(&ME_segpool_sem);
				return (ME_BAD_PARAM);
			}

			status = CloseHandle(seginf->mem_handle);

			if (status == FALSE) {
				status = GetLastError();
				SETCLOS2ERR(err_code, status, ER_close);
				gen_Vsem(&ME_segpool_sem);
				return (ME_BAD_PARAM);
			}

			/*
			** if just attaching to existing memory,
			** there will just be a memory handle, but
			** the file_handle will be NULL.
			*/
			if ( seginf->file_handle )
			{
			    FlushFileBuffers(seginf->file_handle);
			    status = CloseHandle(seginf->file_handle);
			}

			if (status == FALSE) {
				status = GetLastError();
				SETCLOS2ERR(err_code, status, ER_close);
				gen_Vsem(&ME_segpool_sem);
				return (ME_BAD_PARAM);
			} else {
				status = OK;
			}

			next_queue = ME_rem_seg(seginf);
			*addr = (PTR) NULL;
		}
	}
	gen_Vsem(&ME_segpool_sem);
	return status;
}
Example #4
0
STATUS
ME_alloc_shared(i4          flag,
                SIZE_TYPE   pages,
                char        *key,
                PTR         *memory,
                SIZE_TYPE   *allocated_pages,
                CL_ERR_DESC *err_code)
{
	STATUS          status;
	SIZE_TYPE       memsize;
#ifdef LP64
	LARGE_INTEGER	numbytes;
#endif
	HANDLE          name;
	HANDLE          map;
	PTR             temp;
	char            map_key[MAX_LOC+1];
	char            *install_code;
	char		*ObjectPrefix;

    SECURITY_ATTRIBUTES sa;
    
	CLEAR_ERR(err_code);
	GVshobj(&ObjectPrefix);

	if (key == NULL || *key == '\0') {
		return (ME_BAD_PARAM);
	}
	memsize = pages * ME_MPAGESIZE;
        /*
        **  Moved ME_makekey to be called each time ME_alloc_shared is called
        **  as this obtains a handle to the file which will be required later
        **  if this is an attach to shared memory.
        **  This file handle is closed during an MEshared_free.
        */
        if ((name = ME_makekey(key)) == (HANDLE) -1)
        {
            status = GetLastError();
            SETWIN32ERR(err_code, status, ER_alloc);
            return (FAIL);
        }

	/* 
	**  The file mapping key used to be the name of the file.
	**  This caused problems when Jasmine and Ingres were installed
	**  on the same machine.  Create a unique key name, and use
	**  that for File Mapping instead.
	*/

	NMgtAt("II_INSTALLATION", &install_code);
    STpolycat(4, ObjectPrefix, SystemVarPrefix, install_code,
		      key, map_key);

	if (flag & ME_CREATE_MASK) {
	        iimksecdacl( &sa );
		FlushFileBuffers(name);
#ifdef LP64
		numbytes.QuadPart = Int32x32To64(pages, ME_MPAGESIZE);
		map = CreateFileMapping(name,
		                        &sa,
		                        PAGE_READWRITE,
		                        numbytes.HighPart,
		                        numbytes.LowPart,
		                        map_key);
#else
		map = CreateFileMapping(name,
		                        &sa,
		                        PAGE_READWRITE,
		                        0,
		                        memsize,
		                        map_key);
#endif  /* LP64 */

		if (map == NULL) {
			status = GetLastError();
			SETWIN32ERR(err_code, status, ER_alloc);
			FlushFileBuffers(name);
			CloseHandle(name);
			switch (status) {
			case ERROR_ALREADY_EXISTS:
				return ME_ALREADY_EXISTS;
			case ERROR_NOT_ENOUGH_MEMORY:
				return ME_OUT_OF_MEM;
			default:
				return FAIL;
			}
		}

		if (map != NULL && GetLastError() == ERROR_ALREADY_EXISTS) {
			FlushFileBuffers(name);
			CloseHandle(map);
			CloseHandle(name);
			return (ME_ALREADY_EXISTS);
		}
	} else {
		map = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
		                      FALSE,
		                      map_key);

		if (map == NULL) {
			status = GetLastError();
			SETWIN32ERR(err_code, status, ER_alloc);
			FlushFileBuffers(name);
			CloseHandle(name);
			switch (status) {
			case ERROR_FILE_NOT_FOUND:
				return ME_NO_SUCH_SEGMENT;
			case ERROR_NOT_ENOUGH_MEMORY:
				return ME_OUT_OF_MEM;
			default:
				return FAIL;
			}
		}
	}

	/*
	 * Finally.  Now get a memory address for the sucker.
	 * 
	 * If ME_ADDR_SPEC is set, we'll attempt to hardwire the address; else
	 * we'll take whatever the system gives us.
	 */

	if (flag & ME_ADDR_SPEC) {
		temp = MapViewOfFileEx(map,
		                       FILE_MAP_WRITE | FILE_MAP_READ,
		                       0,
		                       0,
		                       0,
		                       *memory);
		if ((temp == NULL) || (temp != *memory)) {
			status = GetLastError();
			SETWIN32ERR(err_code, status, ER_alloc);
			return (FAIL);
		}
	} else {
		*memory = MapViewOfFile(map,
		                        FILE_MAP_WRITE | FILE_MAP_READ,
		                        0,
		                        0,
		                        0);
		if (*memory == NULL) {
			status = GetLastError();
			SETWIN32ERR(err_code, status, ER_alloc);
			return (FAIL);
		}
	}

        /*
        **  If this is not an attach to shared memory assume that pages value
        **  is valid.
        */
        if ((flag & ME_CREATE_MASK) ||
            !(flag & (ME_SSHARED_MASK | ME_MSHARED_MASK)))
        {
	    pages = (SIZE_TYPE)(memsize / ME_MPAGESIZE);
        }
        else
        {
        BY_HANDLE_FILE_INFORMATION sFileInfo;

            /*
            **  If attaching to shared memory ignore the page argument and
            **  calculate size of shared segment in pages from file size.
            **  Assume that a single shared memory file will not exceed 4G.
            */

            if (GetFileInformationByHandle (name, &sFileInfo) == 0)
            {
                status = GetLastError();
                SETWIN32ERR(err_code, status, ER_alloc);
                return (FAIL);
            }
            else
            {
                pages = sFileInfo.nFileSizeLow / ME_MPAGESIZE;
            }
        }


	if (allocated_pages)
		*allocated_pages = pages;

        /*
        **  if this is an attach where pages is 0 ME_reg_seg will register
        **  pages calculated from the size of the file.
        */
	if (ME_reg_seg(*memory,
	               pages,
	               map,
	               name) != OK) {
		UnmapViewOfFile(memory);
		*memory = (PTR) NULL;
		return (FAIL);
	}

	return (OK);
}
Example #5
0
/*
** Name: GClanman_async_thread
** Description:
**	This thread handles all the asynchronous I/O for a protocol driver.
** 	It will be woken up when GClanman() places a request on it's input
**	Q.  Then, this thread will move the request from the input Q to its
**	processing Q and continue to process the request until complete.
**	When complete, the request is finally moved to the completion
**	Q.
** History:
**	04-Nov-93 (edg)
**	    Written.
**	29-jun-2000 (somsa01)
**	    Use GCc_listen_port for the server ncb_name. Also, make sure
**	    that we update GCc_client_name if we need a unique one.
**      06-Aug-2009 (Bruce Lunsford) Sir 122426
**	    Since _beginthreadex() is now used to start this thread,
**	    use _endthreadex() to end it.
*/
VOID
GClanman_async_thread( VOID * parms)
{
	int             status = OK;
	char            callname[NCBNAMSZ+1];
	DWORD		wait_stat;
	HANDLE		hSave;
	int		processing_requests = 0;
	int		pending_requests = 0;
	QUEUE		*q;
	SECURITY_ATTRIBUTES sa;

	iimksec (&sa);

	GCTRACE(4)("LMAN THREAD: started.\n");
top:
	/*
	** Wait for a request to come in from the primary gcc thread....
	*/

	GCTRACE(4)("LMAN THREAD: waiting for event ... \n");

	wait_stat = WaitForSingleObject( hEventThreadInQ, INFINITE );

	GCTRACE(3)("LMAN THREAD: wait returned %d, handle = %d\n", 
		    wait_stat, hEventThreadInQ );

	/*
	** If wait failed, chances are it's a major hosure.  Continue on any
	** way -- there's a possibility that something useful may get done.
	*/
	if (wait_stat == WAIT_FAILED)
	{
	    GCTRACE(1)("LMAN THREAD: wait failed %d\n", 
	    		GetLastError() );
	}

	/*
	** Now get get the incoming requests and add up how many requests
	** we're processing.
	*/
	processing_requests = GCget_incoming_reqs( Tptr, hMutexThreadInQ );

	GCTRACE(2)("LMAN THREAD: Got %d new requests to process\n",
		    processing_requests);

	/*
	** Loop until there's no more requests being processed.
	*/
	while( processing_requests )
	{
            pending_requests = 0;
	    /*
	    ** Now loop thru the inprocess request list.
	    */
	    for ( q = Tptr->process_head.q_next;
	    	  q != &Tptr->process_head;
		  q = q->q_next )
	    {
	        REQUEST_Q *rq = (REQUEST_Q *)q;
		GCC_P_PLIST *parm_list = rq->plist;
		PCB *pcb = (PCB *)parm_list->pcb;

		parm_list->generic_status = OK;
		CLEAR_ERR(&parm_list->system_status);

		switch (parm_list->function_invoked) 
		{

		    /******************************************************
		    ** Handle CONNECT
		    *******************************************************/
		    case GCC_CONNECT:

		    GCTRACE(4)("LMAN THREAD: process CONNECT\n");
		    if ( pcb == NULL || pcb->state.conn == INITIAL )
		    {
		        GCTRACE(3)("LMAN THREAD: initial CONNECT\n");
		        /*
			** Allocate the protocol control block.
			*/
		  	pcb = (PCB *) malloc( sizeof(PCB) );
		    	parm_list->pcb = (char *)pcb;
			if (pcb == NULL) 
			{
			    status = errno;
			    SETWIN32ERR(&parm_list->system_status, status, ER_alloc);
			    pcb->state.conn = COMPLETED;
			    parm_list->generic_status = GC_CONNECT_FAIL;
			    break;
		   	}
			memset( pcb, 0, sizeof( *pcb ) );
		        GCTRACE(3)("LMAN THREAD: CONNECT allocated pcb\n");
			/*
			** Create send/recv event handles for ncb.
			*/
    			if ((pcb->s_ncb.ncb_event = 
				CreateEvent( &sa, TRUE, FALSE, NULL ))== NULL)
    			{
        		    status = GetLastError();
			    pcb->state.conn = COMPLETED;
			    SETWIN32ERR(&parm_list->system_status, status, ER_create);
			    parm_list->generic_status = GC_CONNECT_FAIL;
			    break;
    		        }
       		 	if ((pcb->r_ncb.ncb_event = 
				CreateEvent( &sa, TRUE, FALSE, NULL ))== NULL)
    			{
        		    status = GetLastError();
			    CloseHandle( pcb->s_ncb.ncb_event );
			    pcb->state.conn = COMPLETED;
			    SETWIN32ERR(&parm_list->system_status, status, ER_create);
			    parm_list->generic_status = GC_CONNECT_FAIL;
			    break;
    			}
		        GCTRACE(3)("LMAN THREAD: CONNECT created events\n");

			pcb->state.conn = INITIAL;

		    } /* end if pcb NULL */

		    /*
		    ** If the PCB state is not INITIAL, just break because
		    ** we're just waiting for connect to complete.
		    */
		    if ( pcb->state.conn != INITIAL )
		        break;

		    /*
		    ** Use the send ncb in pcb for the connect -- add name.
		    */
		    pcb->s_ncb.ncb_command = NCBADDNAME;
		    pcb->s_ncb.ncb_buffer = Dummy_Buf;
		    pcb->s_ncb.ncb_length = sizeof(Dummy_Buf);
		    pcb->s_ncb.ncb_lana_num = lana_num;

		    for (;;)
		    {
			STprintf( GCc_client_name, "%s%-d", 
				  MyName, GCc_client_count++ );
			STcopy( GCc_client_name, pcb->s_ncb.ncb_name );

			GCTRACE(3)("LMAN THREAD: CONNECT doing ADDNAME %s\n",
				   pcb->s_ncb.ncb_name );

			/*
			** Copy to local NCB struct -- Netbios seems to fark
			** up if we don't.
			*/
			memcpy( &Name_Ncb, &pcb->s_ncb, sizeof( Name_Ncb ) );

			Netbios( &Name_Ncb );

			if (Name_Ncb.ncb_retcode == NRC_GOODRET)
			    break;
			else if (Name_Ncb.ncb_retcode == NRC_DUPNAME)
			    continue;
			else
			{
			    status = (STATUS)Name_Ncb.ncb_retcode;
			    CloseHandle( Name_Ncb.ncb_event );
			    pcb->s_ncb.ncb_event = NULL;
			    CloseHandle( pcb->r_ncb.ncb_event );
			    pcb->r_ncb.ncb_event = NULL;
			    pcb->state.conn = COMPLETED;
			    SETWIN32ERR(&parm_list->system_status, status,
					ER_netbios);
			    parm_list->generic_status = GC_CONNECT_FAIL;
			    break;
			}
		    }

		    if (parm_list->generic_status == GC_CONNECT_FAIL)
			break;

		    /*
		    ** just in case ...
		    */
		    ResetEvent( pcb->s_ncb.ncb_event );

		    /*
		    ** OK, now make the call
		    */
		    hSave = pcb->s_ncb.ncb_event;    /* save handle */
		    memset( &pcb->s_ncb, 0, sizeof(NCB) );
		    pcb->s_ncb.ncb_event = hSave;    /* restore handle */
		    pcb->s_ncb.ncb_buffer = parm_list->buffer_ptr;    
		    pcb->s_ncb.ncb_length = (WORD)parm_list->buffer_lng;    
		    pcb->s_ncb.ncb_command = NCBCALL | ASYNCH;
		    pcb->s_ncb.ncb_lana_num = lana_num;
		    STcopy( GCc_client_name, pcb->s_ncb.ncb_name );
		    STpolycat( 3, parm_list->function_parms.connect.node_id,
		    	       "_", parm_list->function_parms.connect.port_id,
			       callname );
		    CVupper( callname );

		    
		    /*
		    ** Loopback check to prevent mangling the name (?)
		    */
		    if ( STcompare( parm_list->function_parms.connect.port_id,
		    		    GCc_listen_port ) == 0 )
		    {
		        STcopy( GCc_listen_port, pcb->s_ncb.ncb_callname );
		    }
		    else
		    {
		        STcopy( callname, pcb->s_ncb.ncb_callname );
		    }

		    GCTRACE(3)("LMAN THREAD: CONNECT doing CALL to %s\n",
		    		pcb->s_ncb.ncb_callname );

		    if ( Netbios( &pcb->s_ncb ) != NRC_GOODRET )
		    {
		        status = (STATUS)pcb->s_ncb.ncb_retcode;
			CloseHandle( pcb->s_ncb.ncb_event );
			pcb->s_ncb.ncb_event = NULL;
			CloseHandle( pcb->r_ncb.ncb_event );
			pcb->r_ncb.ncb_event = NULL;
			pcb->state.conn = COMPLETED;
			SETWIN32ERR(&parm_list->system_status, status, ER_netbios);
			parm_list->generic_status = GC_CONNECT_FAIL;
			break;
		    }

		    GCTRACE(3)("LMAN THREAD: Async CALL OK\n" );

		    pcb->state.conn = COMPLETING;

		    break;


		    /*******************************************************
		    ** Handle SEND
		    *******************************************************/
		    case GCC_SEND:

		    GCTRACE(4)("LMAN THREAD: process SEND\n");

		    if ( pcb->state.send != INITIAL )
		    {
		        break;
		    }
		    pcb->s_ncb.ncb_buffer = parm_list->buffer_ptr;    
		    pcb->s_ncb.ncb_length = (WORD)parm_list->buffer_lng;    
		    pcb->s_ncb.ncb_lana_num = lana_num;
		    pcb->s_ncb.ncb_command = NCBSEND | ASYNCH;
		    if ( Netbios( &pcb->s_ncb ) != NRC_GOODRET )
		    {
		        status = (STATUS)pcb->s_ncb.ncb_retcode;
			pcb->state.send = COMPLETED;
			SETWIN32ERR(&parm_list->system_status, status, ER_netbios);
			parm_list->generic_status = GC_SEND_FAIL;
		    }

		    pcb->state.send = COMPLETING;

		    break;


		    /*******************************************************
		    ** Handle RECEIVE
		    *******************************************************/
		    case GCC_RECEIVE:

		    GCTRACE(4)("LMAN THREAD: process RECEIVE\n");

		    if ( pcb->state.recv != INITIAL )
		    {
                        pending_requests++;
		        break;
		    }
		    pcb->r_ncb.ncb_buffer = parm_list->buffer_ptr;    
		    pcb->r_ncb.ncb_length = (WORD)parm_list->buffer_lng;    
		    pcb->r_ncb.ncb_lana_num = lana_num;
		    pcb->r_ncb.ncb_command = NCBRECV | ASYNCH;

		    if ( Netbios( &pcb->r_ncb ) != NRC_GOODRET )
		    {
		        status = (STATUS)pcb->r_ncb.ncb_retcode;
			pcb->state.recv = COMPLETED;
			SETWIN32ERR(&parm_list->system_status, status, ER_netbios);
			parm_list->generic_status = GC_RECEIVE_FAIL;
		    }

		    pcb->state.recv = COMPLETING;

		    break;

		    /*******************************************************
		    ** Handle DISCONNECT
		    *******************************************************/
		    case GCC_DISCONNECT:

		    GCTRACE(4)("LMAN THREAD: process DISCONNECT\n");

		    if ( pcb && pcb->state.disc == INITIAL ) 
		    {
		        pcb->s_ncb.ncb_buffer = parm_list->buffer_ptr;    
		        pcb->s_ncb.ncb_length = (WORD)parm_list->buffer_lng;    
		        pcb->s_ncb.ncb_command = NCBHANGUP | ASYNCH;
		    	pcb->s_ncb.ncb_lana_num = lana_num;
			if ( pcb->s_ncb.ncb_lsn == 0 )
			    pcb->s_ncb.ncb_lsn = pcb->r_ncb.ncb_lsn;
		        if ( Netbios( &pcb->s_ncb ) != NRC_GOODRET )
		        {
		            status = (STATUS)pcb->s_ncb.ncb_retcode;
		    	    pcb->state.disc = COMPLETED;
			    SETWIN32ERR(&parm_list->system_status, status, ER_netbios);
			    parm_list->generic_status = GC_DISCONNECT_FAIL;
			    break;
		        }
		        pcb->state.disc = COMPLETING;
		    }

		    break;

		} /* end switch */

	    } /* end for process q loop */

	    /*
	    ** Now go thru the inprocess Q and look for any requests that
	    ** have been completed.  This will be indicated by one of:
	    ** parm_list->pcb == NULL (bad connect or after disconnect) or
	    ** pcb->state == COMPLETED, or WaitForSingleObject indicates
	    ** completion.
	    */

	    GCTRACE(4)("LMAN THREAD: processing completed. . . \n");

	    q = Tptr->process_head.q_next;
	    while( q != &Tptr->process_head )
	    {
	        REQUEST_Q *rq = (REQUEST_Q *)q;
	        GCC_P_PLIST *pl = rq->plist;
		PCB *pcb = (PCB *)pl->pcb;
		bool completed = FALSE;

		switch ( pl->function_invoked )
		{
		    case GCC_CONNECT:
		        if ( pcb == NULL || pcb->state.conn == COMPLETED || 
			     WaitForSingleObject( pcb->s_ncb.ncb_event, 0) ==
			         WAIT_OBJECT_0 )
			{
			    if (pcb) 
			    {
			        ResetEvent( pcb->s_ncb.ncb_event );
			        pcb->r_ncb.ncb_lsn = pcb->s_ncb.ncb_lsn;
				if ( pcb->s_ncb.ncb_lsn == 0 ||
				     pcb->s_ncb.ncb_retcode != NRC_GOODRET )
				{
				    pl->generic_status = GC_CONNECT_FAIL;
				    status = (STATUS)pcb->s_ncb.ncb_retcode;
				    SETWIN32ERR( &pl->system_status, status , ER_revent);
				    CloseHandle( pcb->s_ncb.ncb_event );
				    CloseHandle( pcb->r_ncb.ncb_event );
				    free( pcb );
				    pl->pcb = NULL;
				}
			    }

			    completed = TRUE;
			}
		        break;
		    case GCC_SEND:
		        if ( pcb == NULL || pcb->state.send == COMPLETED || 
			     WaitForSingleObject( pcb->s_ncb.ncb_event, 0) ==
			         WAIT_OBJECT_0 )
			{
			    ResetEvent( pcb->s_ncb.ncb_event );
			    if ( pcb->s_ncb.ncb_lsn == 0 ||
			         pcb->s_ncb.ncb_retcode != NRC_GOODRET )
			    {
				    pl->generic_status = GC_SEND_FAIL;
				    status = (STATUS)pcb->s_ncb.ncb_retcode;
				    SETWIN32ERR( &pl->system_status, status , ER_revent);
			    }
			    else
			    {
			        GCTRACE(2)(
				"LMAN THREAD: Send COMP pl len %d pcb len %d\n",
			            pl->buffer_lng, pcb->s_ncb.ncb_length);
					
			        pl->buffer_lng = pcb->s_ncb.ncb_length;
			    }
			    completed = TRUE;
			}
		        break;
		    case GCC_RECEIVE:
		        if ( pcb == NULL || pcb->state.recv == COMPLETED || 
			     WaitForSingleObject( pcb->r_ncb.ncb_event, 0) ==
			         WAIT_OBJECT_0 )
			{
			    ResetEvent( pcb->r_ncb.ncb_event );
			    if ( pcb->s_ncb.ncb_lsn == 0 ||
			         pcb->r_ncb.ncb_retcode != NRC_GOODRET )
			    {
				    pl->generic_status = GC_RECEIVE_FAIL;
				    status = (STATUS)pcb->r_ncb.ncb_retcode;
				    SETWIN32ERR( &pl->system_status, status , ER_revent);
			    }
			    else
			    {
			        pl->buffer_lng = pcb->r_ncb.ncb_length;
			    }
			    completed = TRUE;
			}
		        break;
		    case GCC_DISCONNECT:
		        if ( pcb == NULL || pcb->state.disc == COMPLETED || 
			     WaitForSingleObject( pcb->s_ncb.ncb_event, 0) ==
			         WAIT_OBJECT_0 )
			{
			    if (pcb) 
			    {
				if ( pcb->s_ncb.ncb_lsn == 0 ||
				     pcb->s_ncb.ncb_retcode != NRC_GOODRET )
				{
				    pl->generic_status = GC_DISCONNECT_FAIL;
				    status = (STATUS)pcb->s_ncb.ncb_retcode;
				    SETWIN32ERR( &pl->system_status, status , ER_revent);
				}
				pcb->s_ncb.ncb_command = NCBDELNAME;
				Netbios( &pcb->s_ncb );
				CloseHandle( pcb->s_ncb.ncb_event );
				CloseHandle( pcb->r_ncb.ncb_event );
				free( pcb );
				pl->pcb = NULL;
			    }
			    completed = TRUE;
			}
		        break;
		} /* end switch */

		if ( completed )
		{
		    QUEUE *nq = q->q_next;

	    	    GCTRACE(3)("LMAN THREAD:  Complete! PCB = %x PARM = %x \n",
		    		pcb, pl);

		    GCcomplete_request( q );

		    q = nq;
		    processing_requests--;

	    	    GCTRACE(3)("LMAN THREAD: processed completed \n");
	    	    GCTRACE(3)("                 : total now = %d \n",
		    		processing_requests);

		} /* end if req completed */
		else
		{
		    q = q->q_next;
		}

	    } /* end for -- look for complete req */

	    /*
	    ** Do a quick, non-blocking check to see if any new requests
	    ** came in during processing.
	    */
	    GCTRACE(4)("LMAN THREAD: quick look for new reqs \n");
	    if ( WaitForSingleObject( hEventThreadInQ, 0 ) == WAIT_OBJECT_0 )
	    {
		processing_requests += GCget_incoming_reqs( Tptr, 
					   hMutexThreadInQ );
	    }
	    GCTRACE(4)("LMAN THREAD: process reqs now = %d\n",
	    		processing_requests);

            if (processing_requests && pending_requests == processing_requests)
            {
                i4 Sleeptime = 1;
                Sleep(Sleeptime);
            }

	} /* end while processing requests */

	if (In_Shutdown)
	{
		_endthreadex(0);
		return;
	}

	/*
	** we're done for now, go back to the top and sleep.
	*/
	GCTRACE(3)("LMAN THREAD: No more reqs, going back to top\n" );
	goto top;
}
Example #6
0
/*
** Name: GClanman
** Description:
**	Main entry point for the window's NT lan manager protocol driver.  This
** 	driver is essentially just a dispatcher -- it runs in the primary
**	GCC thread and mostly just Q's things to do to the constantly running
**	aynchronous request thread.  It may also start a listen thread if
**	it is a LISTEN request.
**
**	The following functions are handled:
**	    GCC_OPEN	- call GClanman_open
**	    GCC_LISTEN  - start listen thread
**	    GCC_SEND    - Q request for asynch thread
**	    GCC_RECEIVE - Q request for asynch thread
**	    GCC_CONNECT - Q request for asynch thread
**	    GCC_DISCONN - Q request for asynch thread
** History:
**	11-Nov-93 (edg)
**	    Original.
**      06-Aug-2009 (Bruce Lunsford) Sir 122426
**          Remove mutexing around calls to GCA service completion routine
**          as it is no longer necessary, since GCA is thread-safe...removes
**          calls to GCwaitCompletion + GCrestart. Should improve peformance.
**	    Convert CreateThread() to _beginthreadex() which is recommended
**	    when using C runtime.
*/
STATUS
GClanman( i4 function_code, GCC_P_PLIST * parm_list)
{
	STATUS          generror = 0;
	int             status = 0;
	int             tid;
	HANDLE          hThread;
    	REQUEST_Q 	*rq;
	SECURITY_ATTRIBUTES sa;

	iimksec (&sa);

	CLEAR_ERR(&parm_list->system_status);

	/*
	** set error based on function code and determine whether we got a
	** valid function.
	*/
	switch (function_code) {
	case GCC_OPEN:
		is_comm_svr = TRUE;
		GCTRACE(2) ("GClanman: Function = OPEN\n" );
		return GClanman_open( parm_list );

	case GCC_LISTEN:
		GCTRACE(2) ("GClanman: Function = LISTEN\n" );
		generror = GC_LISTEN_FAIL;
                /*
                ** For Lanman, the peer is always remote.
                */
                parm_list->options = 0;

		/*
		** Spawn off a thread to handle the listen request
		*/
		hThread = (HANDLE)_beginthreadex(&sa,
			       GC_STACK_SIZE,
			       (LPTHREAD_START_ROUTINE) GClanman_listen,
			       parm_list,
			       (unsigned long)NULL,
			       &tid);
		if (hThread) 
		{
			CloseHandle(hThread);
			return (OK);
		}
		status = errno;
		SETWIN32ERR(&parm_list->system_status, status, ER_create);
		goto err_exit;
		break;

	case GCC_CONNECT:
	    GCTRACE(2) ("GClanman: Function = CONNECT\n" );
	    generror = GC_CONNECT_FAIL;
	    break;
	case GCC_SEND:
	    GCTRACE(2) ("GClanman: Function = SEND\n" );
	    generror = GC_SEND_FAIL;
	    break;
	case GCC_RECEIVE:
	    GCTRACE(2) ("GClanman: Function = RECEIVE\n" );
	    generror = GC_RECEIVE_FAIL;
	    break;
	case GCC_DISCONNECT:
	    GCTRACE(2) ("GClanman: Function = DISCONNECT\n" );
	    generror = GC_DISCONNECT_FAIL;
	    break;

	default:
	    return FAIL;
	}			/* end switch */
	/*
	** CONNECT, SEND, RECEIVE and DISCONNECT are all dispatched
	** to the asynch thread.
   	** Now allocate a request q structure, stick it into incoming q,
   	** and raise the INCOMING REQUEST event.
   	*/
	GCTRACE(2)("GClanman: Q'ing request ...\n");

    	if ( (rq = (REQUEST_Q *)MEreqmem(0, sizeof(*rq), TRUE, NULL ) ) != NULL )
    	{
            rq->plist = parm_list;
	    /*
	    ** get mutex for completion Q
	    */
	    GCTRACE(2)("GClanman: wait for input mutex ...\n");
	    WaitForSingleObject( hMutexThreadInQ, INFINITE );

	    /*
	    ** Now insert the completed request into the inconming Q.
	    */
	    GCTRACE(2)("GClanman: inserting incoming req ...\n");
	    QUinsert( &rq->req_q, &Tptr->incoming_head );

	    /*
	    ** release mutex for completion Q
	    */
	    GCTRACE(2)("GClanman: releasing Mutex incoming req ...\n");
	    ReleaseMutex( hMutexThreadInQ );

	    /*
	    ** raise the incoming event to wake up the thread.
	    */
	    GCTRACE(2)("GClanman: Setting event ...\n");
	    if ( !SetEvent( hEventThreadInQ ) )
	    {
	   	    status = GetLastError();
		    SETWIN32ERR(&parm_list->system_status, status, ER_sevent);
	   	    GCTRACE(1)("GClanman, SetEvent error = %d\n", 
		    		status );
	    }

	    return OK;

   	}
    	else
    	{
	        /*
		** MEreqmem failed
		*/
		SETWIN32ERR(&parm_list->system_status, errno, ER_alloc);
    	}

	/*
	 * * Drive the completion routine on error
	 */
err_exit:
	parm_list->generic_status = generror;
	(*parm_list->compl_exit) (parm_list->compl_id);
	return OK;
}
Example #7
0
/******************************************************************************
**
** Name: DI_list - Use WIN32 NT directory listing interface to list files
**
** Description:
**	List all files/directories in a given directory depending on
**	list_type argument.
**
** Inputs:
**      path                 Pointer to the path name.
**      pathlength           Length of path name.
**	    list_type	         type of object to list (DIRECTORY or FILE)
**	    func		         function to call for each object found.
**	    arg_list	         arguments to call the function with.
**
** Outputs:
**      err_code             Pointer to a variable used
**                           to return operating system
**                           errors.
**
**    Returns:
**        DI_BADDIR          Path specification error.
**        DI_ENDFILE         Error returned from client handler or
**                           all files listed.
**        DI_BADPARAM        Parameter(s) in error.
**        DI_DIRNOTFOUND     Path not found.
**        DI_BADLIST         Error trying to list objects.
**
**    Exceptions:
**        none
**
** Side Effects:
**        none
**
** History:
**	07-jul-1995 (canor01)
**	    convert filename returned from FindFirstFile() to lowercase, since
**	    other DI functions (via unix) expect lowercase.
**      18-jul-1995 (reijo01)
**          Changed SETWIN32ERR so that it will populate the CL_ERR_DESC with
**              the proper values.
**      08-dec-1997 (canor01)
**          Implement LRU for open files (initial copy from Unix).
**	28-jan-1998 (canor01)
**	    Correct a problem with previous implementation.  If closing
**	    files succeeds, retry, else bail out with error.
**	04-mar-1998 (canor01)
**	    Parameters to DIlru_flush were changed on Unix side.  Change
**	    them here too.
**
******************************************************************************/
static STATUS
DI_list(char       *path,
        u_i4       pathlength,
        i4         list_type,
        STATUS     (*func) (),
        PTR        arg_list,
        CL_SYS_ERR *err_code)
{
	STATUS          ret_val = OK;
	STATUS          dos_call;

	char            input_path[DI_PATH_MAX];
	HANDLE          handle;
	ULONG           count = 1;
	WIN32_FIND_DATA findbuf;

	/* default returns */

	CLEAR_ERR(err_code);

	/* Check input parameters */

	if ((pathlength > DI_PATH_MAX) ||
	    (pathlength == 0))
		return DI_BADPARAM;

	/* get null terminated path to the directory */

	memcpy(input_path, path, pathlength);
	memcpy(input_path + pathlength, "\\*.*", 5);

	/* break out on errors */

	while ( (handle = FindFirstFile(input_path, &findbuf)) == 
		INVALID_HANDLE_VALUE )
	{
		switch (dos_call = GetLastError()) 
		{
            	case ERROR_NO_SYSTEM_RESOURCES:
                case ERROR_TOO_MANY_OPEN_FILES:
                case ERROR_NO_MORE_FILES:
                case ERROR_NOT_ENOUGH_MEMORY:
                case ERROR_OUTOFMEMORY:
                case ERROR_HANDLE_DISK_FULL:
                case ERROR_OUT_OF_STRUCTURES:
                case ERROR_NONPAGED_SYSTEM_RESOURCES:
                case ERROR_PAGED_SYSTEM_RESOURCES:
            		if (DIlru_flush( err_code ) != OK)
            		{
                	    ret_val = DI_BADLIST;
            		}
			else
			{
			    continue;
			}
                	break;
		case ERROR_PATH_NOT_FOUND:
			SETWIN32ERR(err_code, dos_call, ER_list);
			ret_val = DI_DIRNOTFOUND;
			break;
		default:
			SETWIN32ERR(err_code, dos_call, ER_list);
			ret_val = DI_BADLIST;
			break;
		}
		if ( ret_val != OK )
		    return(ret_val);
	}

	/* now loop through all entries in the directory */

	ret_val = DI_ENDFILE;

	do {
		if (!memcmp(findbuf.cFileName, ".", 2) ||
		    !memcmp(findbuf.cFileName, "..", 3)) 
		{
			continue;
		}

		/*
		 * This next check is a leftover from OS/2.  Should dump it
		 * at some point, or enlarge the functionality of this
		 * routine. If we're looking for a directory, verify it is
		 * one...
		 */

		if (list_type == DIRECTORY) 
		{
			if (~findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				continue;
			}
		}

		/* 
		* other functions expect lowercase name, but DOS
		* returns uppercase
                *
                * bug fix 89213.  
                * Unlike DOS, NT returns upper and lower case
                * names.
		*/
		if ((*func) (arg_list,
		             findbuf.cFileName,
		             STlength(findbuf.cFileName),
		             err_code)) 
		{
			ret_val = DI_ENDFILE;
			break;
		}
	} while (FindNextFile(handle, &findbuf));

	if (!FindClose(handle)) 	
	{
		SETWIN32ERR(err_code, GetLastError(), ER_list);
		ret_val = DI_BADLIST;
	}

	return (ret_val);
}
Example #8
0
int serval_crypto_handler(co_obj_t *self, co_obj_t **output, co_obj_t *params) {
  CLEAR_ERR();
  
  int list_len = co_list_length(params), keypath = 0;
  
  CHECK_ERR(IS_LIST(params) && list_len >= 2,"Invalid params");
  
  if (!strncmp("--keyring=",co_obj_data_ptr(co_list_get_last(params)),10)) {
    keypath = 1;
    --list_len;
  }
  
  if (co_str_cmp_str(co_list_element(params,0),"sign") == 0) {
    
    CHECK_ERR(list_len == 2 || list_len == 3,"Invalid arguments");
    char sig_buf[2*SIGNATURE_BYTES + 1] = {0};
    if (list_len == 3) {
      CHECK_ERR(cmd_serval_sign(_LIST_ELEMENT(params,1),
			co_str_len(co_list_element(params,1)) - 1,
			(unsigned char*)_LIST_ELEMENT(params,2),
			co_str_len(co_list_element(params,2)) - 1,
			sig_buf,
			2*SIGNATURE_BYTES + 1,
			keypath ? _LIST_ELEMENT(params,3) + 10 : NULL, // strlen("--length=") == 10
			keypath ? co_str_len(co_list_element(params,3)) - 11 : 0),"Failed to create signature");
    } else if (list_len == 2) {
      CHECK_ERR(cmd_serval_sign(NULL,
			0,
			(unsigned char*)_LIST_ELEMENT(params,1),
			co_str_len(co_list_element(params,1)) - 1,
			sig_buf,
			2*SIGNATURE_BYTES + 1,
			keypath ? _LIST_ELEMENT(params,2) + 10 : NULL, // strlen("--length=") == 10
			keypath ? co_str_len(co_list_element(params,2)) - 11 : 0),"Failed to create signature");
    }
    CMD_OUTPUT("result",co_str8_create(sig_buf,2*SIGNATURE_BYTES+1,0));
    
  } else if (co_str_cmp_str(co_list_element(params,0),"verify") == 0) {
    
    CHECK_ERR(!keypath,"Keyring option not available for verification");
    CHECK_ERR(list_len == 4,"Invalid arguments");
    int verdict = cmd_serval_verify(_LIST_ELEMENT(params,1),
				  co_str_len(co_list_element(params,1)) - 1,
				  (unsigned char*)_LIST_ELEMENT(params,3),
				  co_str_len(co_list_element(params,3)) - 1,
				  _LIST_ELEMENT(params,2),
				  co_str_len(co_list_element(params,2)) - 1);
// 				  keypath ? _LIST_ELEMENT(params,4) + 10 : NULL, // strlen("--length=") == 10
// 				  keypath ? co_str_len(co_list_element(params,4)) - 10 : 0);
    if (verdict == 1) {
      DEBUG("signature verified");
      CMD_OUTPUT("result",co_bool_create(true,0));  // successfully verified
    } else if (verdict == 0) {
      DEBUG("signature NOT verified");
      CMD_OUTPUT("result",co_bool_create(false,0));
    }
    
  }
  
  return 1;
error:
  INS_ERROR();
  return 0;
}
Example #9
0
int
serval_crypto_handler(co_obj_t *self, co_obj_t **output, co_obj_t *params)
{
  CLEAR_ERR();
  
  svl_crypto_ctx *ctx = NULL;
  int list_len = co_list_length(params);
  int keypath = 0;
  
  CHECK_ERR(IS_LIST(params) && list_len >= 2, "Invalid params");
  
  if (!strncmp("--keyring=", co_obj_data_ptr(co_list_get_last(params)), 10)) {
    keypath = 1;
    --list_len;
  }
  
  ctx = svl_crypto_ctx_new();
  
  if (co_str_cmp_str(co_list_element(params, 0), "sign") == 0) {
    
    CHECK_ERR(list_len == 2 || list_len == 3, "Invalid arguments");
    
    if (list_len == 3) {
      char *sid_str = _LIST_ELEMENT(params, 1);
      size_t sid_len = co_str_len(co_list_element(params, 1)) - 1;
      CHECK_ERR(sid_len == (2 * SID_SIZE) && str_is_subscriber_id(sid_str) == 1,
		"Invalid SID");
      stowSid(ctx->sid, 0, sid_str);
      ctx->msg = (unsigned char*)_LIST_ELEMENT(params, 2);
      ctx->msg_len = co_str_len(co_list_element(params, 2)) - 1;
      if (keypath) {
	ctx->keyring_path = _LIST_ELEMENT(params, 3) + 10;
	ctx->keyring_len = co_str_len(co_list_element(params, 3)) - 11;
	CHECK_ERR(ctx->keyring_len < PATH_MAX,"Keyring path too long");
      }
    
    } else if (list_len == 2) {
      
      ctx->msg = (unsigned char*)_LIST_ELEMENT(params, 1);
      ctx->msg_len = co_str_len(co_list_element(params, 1)) - 1;
      if (keypath) {
	ctx->keyring_path = _LIST_ELEMENT(params, 2) + 10;
	ctx->keyring_len = co_str_len(co_list_element(params, 2)) - 11;
	CHECK_ERR(ctx->keyring_len < PATH_MAX,"Keyring path too long");
      }

    }
    CHECK_ERR(cmd_serval_sign(ctx), "Failed to create signature");
    
    // convert ctx->signature, ctx->sas_public, and ctx->sid to hex: 
    char sid_str[(2 * SID_SIZE) + 1] = {0};
    strncpy(sid_str, alloca_tohex(ctx->sid, SID_SIZE), 2 * SID_SIZE);
    char sas_str[(2 * crypto_sign_PUBLICKEYBYTES) + 1] = {0};
    strncpy(sas_str, alloca_tohex(ctx->sas_public, crypto_sign_PUBLICKEYBYTES), 2 * crypto_sign_PUBLICKEYBYTES);
    char sig_str[(2 * SIGNATURE_BYTES) + 1] = {0};
    strncpy(sig_str, alloca_tohex(ctx->signature, SIGNATURE_BYTES), 2 * SIGNATURE_BYTES);
    CMD_OUTPUT("SID", co_str8_create(sid_str, (2 * SID_SIZE) + 1, 0));
    CMD_OUTPUT("SAS", co_str8_create(sas_str, (2 * crypto_sign_PUBLICKEYBYTES) + 1, 0));
    CMD_OUTPUT("signature", co_str8_create(sig_str, (2 * SIGNATURE_BYTES) + 1, 0));
    
  } else if (co_str_cmp_str(co_list_element(params, 0), "verify") == 0) {
    
    CHECK_ERR(!keypath, "Keyring option not available for verification");
    CHECK_ERR(list_len == 4, "Invalid arguments");
    // convert SAS and signature from hex to bin
    CHECK_ERR(fromhexstr(ctx->signature, _LIST_ELEMENT(params, 2), SIGNATURE_BYTES) == 0, "Invalid signature");
    CHECK_ERR(fromhexstr(ctx->sas_public, _LIST_ELEMENT(params, 1), crypto_sign_PUBLICKEYBYTES) == 0, "Invalid SAS key");
    ctx->msg = (unsigned char*)_LIST_ELEMENT(params, 3);
    ctx->msg_len = co_str_len(co_list_element(params, 3)) - 1;

    int verdict = cmd_serval_verify(ctx);
    if (verdict == 1) {
      DEBUG("signature verified");
      CMD_OUTPUT("result", co_bool_create(true, 0));  // successfully verified
      CMD_OUTPUT("verified",co_str8_create("true",sizeof("true"),0));
    } else if (verdict == 0) {
      DEBUG("signature NOT verified");
      CMD_OUTPUT("result", co_bool_create(false, 0));
      CMD_OUTPUT("verified",co_str8_create("false",sizeof("false"),0));
    }
    
  }
  
error:
  INS_ERROR();
  if (ctx)
    svl_crypto_ctx_free(ctx);
  return 1;
}