Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Generate application sources
0003  *
0004  * @file cmd_gen.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 #include <string.h>
0035 #include <stdio.h>
0036 #include <stdlib.h>
0037 #include <memory.h>
0038 #include <sys/param.h>
0039 
0040 #include <ndrstandard.h>
0041 #include <ndebug.h>
0042 
0043 #include <ndrx.h>
0044 #include <ndrxdcmn.h>
0045 #include <atmi_int.h>
0046 #include <gencall.h>
0047 #include <errno.h>
0048 
0049 #include "nclopt.h"
0050 
0051 #include <exhash.h>
0052 #include <utlist.h>
0053 #include <userlog.h>
0054 
0055 #ifndef NDRX_DISABLEPSCRIPT
0056 #include <pscript.h>
0057 #include <psstdblob.h>
0058 #include <psstdio.h>
0059 #include <psstdsystem.h>
0060 #include <psstdmath.h>
0061 #include <psstdstring.h>
0062 #include <psstdexutil.h>
0063 #include <psstdaux.h>
0064 #endif
0065 
0066 #ifndef NDRX_DISABLEPSCRIPT
0067 /*---------------------------Externs------------------------------------*/
0068 
0069 extern const char ndrx_G_resource_gen_go_server[];
0070 extern const size_t ndrx_G_resource_gen_go_server_len;
0071 
0072 extern const char ndrx_G_resource_gen_go_client[];
0073 extern const size_t ndrx_G_resource_gen_go_client_len;
0074 
0075 extern const char ndrx_G_resource_gen_c_client[];
0076 extern const size_t ndrx_G_resource_gen_c_client_len;
0077 
0078 extern const char ndrx_G_resource_gen_c_server[];
0079 extern const size_t ndrx_G_resource_gen_c_server_len;
0080 
0081 extern const char ndrx_G_resource_gen_ubf_tab[];
0082 extern const size_t ndrx_G_resource_gen_ubf_tab_len;
0083 
0084 extern const char ndrx_G_resource_gen_test_local[];
0085 extern const size_t ndrx_G_resource_gen_test_local_len;
0086 
0087 extern const char ndrx_G_resource_gen_java_server[];
0088 extern const size_t ndrx_G_resource_gen_java_server_len;
0089 
0090 extern const char ndrx_G_resource_gen_java_client[];
0091 extern const size_t ndrx_G_resource_gen_java_client_len;
0092 
0093 extern const char ndrx_G_resource_gen_python_client[];
0094 extern const size_t ndrx_G_resource_gen_python_client_len;
0095 
0096 extern const char ndrx_G_resource_gen_python_server[];
0097 extern const size_t ndrx_G_resource_gen_python_server_len;
0098 
0099 /*---------------------------Macros-------------------------------------*/
0100 /* #define GEN_DEBUG 1 */
0101 /*---------------------------Enums--------------------------------------*/
0102 /*---------------------------Typedefs-----------------------------------*/
0103 typedef struct gen_hash gen_hash_t;
0104 struct gen_hash
0105 {
0106     char command[PATH_MAX+1];
0107     
0108     const char *stock; /**< allocated in memory                               */
0109     size_t stock_len; /**< Number of bytes in stock built-in script      */
0110     
0111     char *fname; /**< allocated on disk                                 */
0112     
0113     EX_hash_handle hh; /**< makes this structure hashable               */
0114 };
0115 
0116 /*---------------------------Globals------------------------------------*/
0117 /*---------------------------Statics------------------------------------*/
0118 
0119 exprivate gen_hash_t *M_gen_res = NULL; /* gen resources */
0120 
0121 /*---------------------------Prototypes---------------------------------*/
0122 
0123 /**
0124  * Get the sub-command
0125  * @param sub_command
0126  * @return 
0127  */
0128 exprivate gen_hash_t * gen_get(char *command)
0129 {
0130     gen_hash_t *ret = NULL;
0131     
0132     EXHASH_FIND_STR( M_gen_res, command, ret);
0133     
0134     return ret;
0135 }
0136 
0137 /**
0138  * Add static command
0139  * @param command command name
0140  * @param stock built in script bytes
0141  * @param stock built_len script bytes len
0142  * @param fname file name on disk based script
0143  * @return EXSUCCEED/EXFAIL
0144  */
0145 exprivate int reg_cmd(char *command, const char *stock, size_t stock_len, const char *fname)
0146 {
0147     gen_hash_t *gen;
0148     int ret = EXSUCCEED;
0149     
0150     if (NULL==(gen = NDRX_MALLOC(sizeof(gen_hash_t))))
0151     {
0152         int err = errno;
0153         NDRX_LOG(log_error, "Failed to alloc gen_hash_t: %s", strerror(err));
0154         userlog("Failed to alloc gen_hash_t: %s", strerror(err));
0155         EXFAIL_OUT(ret);
0156     }
0157     
0158     NDRX_STRCPY_SAFE(gen->command, command);
0159     
0160     if (stock)
0161     {
0162         gen->stock = stock;
0163         gen->stock_len = stock_len;
0164     }
0165     else if (fname)
0166     {
0167         if (NULL==(gen->fname = strdup(fname)))
0168         {
0169             int err = errno;
0170             NDRX_LOG(log_error, "Failed to alloc: %s", strerror(err));
0171             userlog("Failed to alloc: %s", strerror(err));
0172             EXFAIL_OUT(ret);
0173         }
0174     }
0175     
0176 #ifdef GEN_DEBUG
0177     fprintf(stderr, "Adding command [%s]", gen->command);
0178 #endif
0179     
0180     /* add stuff to hash 
0181      * TODO: How about duplicate check?
0182      */
0183     EXHASH_ADD_STR( M_gen_res, command, gen );
0184     
0185 out:
0186     
0187     if (EXSUCCEED!=ret)
0188     {
0189         if (gen)
0190         {
0191             if (gen->fname)
0192             {
0193                 free(gen->fname);
0194             }
0195             
0196             free(gen);
0197         }
0198     }
0199     return ret;
0200 }
0201 
0202 /**
0203  * This will load the generation scripts into hash
0204  * Hash shall contain the ptr to symbol in memory
0205  * Or path to file (path loaded from config)
0206  * @return SUCCEED/FAIL
0207  */
0208 expublic int cmd_gen_load_scripts(void)
0209 {
0210     int ret = EXSUCCEED;
0211     ndrx_inicfg_section_keyval_t *val;
0212     string_list_t* flist, *elt= NULL;
0213     int return_status = EXSUCCEED;
0214     
0215     char tmp[PATH_MAX+1];
0216     char path[PATH_MAX+1];
0217     char *start;
0218     char *p;
0219     int i;
0220     
0221 #define EMB_RES(name) name, name##_len
0222     
0223     /* 1. List strings in memory, start with "gen_001" */
0224     if (EXSUCCEED!=reg_cmd("go server", EMB_RES(ndrx_G_resource_gen_go_server), NULL)
0225         || EXSUCCEED!=reg_cmd("go client", EMB_RES(ndrx_G_resource_gen_go_client), NULL)
0226         || EXSUCCEED!=reg_cmd("c server", EMB_RES(ndrx_G_resource_gen_c_server), NULL)
0227         || EXSUCCEED!=reg_cmd("c client", EMB_RES(ndrx_G_resource_gen_c_client), NULL)
0228         || EXSUCCEED!=reg_cmd("java server", EMB_RES(ndrx_G_resource_gen_java_server), NULL)
0229         || EXSUCCEED!=reg_cmd("java client", EMB_RES(ndrx_G_resource_gen_java_client), NULL)
0230         || EXSUCCEED!=reg_cmd("python client", EMB_RES(ndrx_G_resource_gen_python_client), NULL)
0231         || EXSUCCEED!=reg_cmd("python server", EMB_RES(ndrx_G_resource_gen_python_server), NULL)
0232         || EXSUCCEED!=reg_cmd("ubf tab", EMB_RES(ndrx_G_resource_gen_ubf_tab), NULL)
0233         || EXSUCCEED!=reg_cmd("test local", EMB_RES(ndrx_G_resource_gen_test_local), NULL)
0234        )
0235     {
0236         EXFAIL_OUT(ret);
0237     }
0238     
0239     /* 2. List strings on disk, if have such config string */
0240     if (NULL!=(val=ndrx_keyval_hash_get(G_xadmin_config, "gen scripts")))
0241     {
0242         if (NULL!=(flist = ndrx_sys_folder_list(val->val, &return_status)))
0243         {
0244             int len, replaced;
0245             /* Loop over the files & load the into memory 
0246              * If name terminates with .pscript, then load it.
0247              */
0248             
0249             LL_FOREACH(flist,elt)
0250             {
0251                 len = strlen(elt->qname);
0252                 
0253                 if (len>8 && 0==strncmp(elt->qname+(len-8), ".pscript", 8)
0254                         /* name shall match gen_<lang>_<type>.pscript */
0255                     )
0256                 {
0257                     NDRX_STRCPY_SAFE(tmp, elt->qname);
0258                     
0259                     start = strstr(tmp, "gen_");
0260                     
0261                     if (NULL==start)
0262                     {
0263                         /* not ours invalid name.. */
0264                         continue; 
0265                     }
0266                     
0267                     start+=4;
0268                     
0269                     p = strrchr(tmp, '.');
0270                     
0271                     *p = EXEOS; /* do not care the suffix */
0272                     
0273                     /* replace _ with space*/   
0274                     len = strlen(start);
0275                     replaced = 0;
0276                     for (i=0; i<len; i++)
0277                     {
0278                         if ('_' == start[i])
0279                         {
0280                             replaced++;
0281                             start[i]=' ';
0282                         }
0283                     }
0284                     
0285                     if (!replaced)
0286                     {
0287                         /* not ours invalid name.. */
0288                         continue;
0289                     }
0290                     
0291                     if (len==replaced)
0292                     {
0293                         /* not ours invalid name.. */
0294                         continue;
0295                     }
0296                     
0297                     /* ok it is ours.. */
0298                     snprintf(path, sizeof(path), "%s%s", val->val, elt->qname);
0299                     
0300 #ifdef GEN_DEBUG
0301                     fprintf(stdout, "Adding resource: cmd [%s] file [%s]\n", 
0302                             start, path);
0303 #endif
0304                     
0305                     if (EXSUCCEED!=reg_cmd(start, NULL, 0, path))
0306                     {
0307                         EXFAIL_OUT(ret);
0308                     }
0309 
0310                 }
0311             }
0312            
0313             ndrx_string_list_free(flist);
0314         }
0315     }
0316     
0317 out:
0318     
0319     return ret;
0320 }
0321 
0322 /**
0323  * This will list available commands for help callback.
0324  * @return 
0325  */
0326 expublic int cmd_gen_help(void)
0327 {
0328     gen_hash_t *val = NULL, *val_tmp = NULL;
0329     
0330     EXHASH_ITER(hh, M_gen_res, val, val_tmp)
0331     {
0332         fprintf(stdout, "\t\t%s\n", val->command);
0333     }
0334     
0335     fprintf(stdout, "\n");
0336     
0337     return EXSUCCEED;
0338 }
0339 
0340 /**
0341  * File feed for reader from file
0342  * @param file
0343  * @return 
0344  */
0345 PSInteger file_lexfeedASCII(PSUserPointer file)
0346 {
0347     int ret;
0348     char c;
0349     if( ( ret=fread(&c,sizeof(c),1,(FILE *)file )>0) )
0350     {
0351         return c;
0352     }
0353     return 0;
0354 }
0355 
0356 /**
0357  * Generate application sources
0358  * We will dynamically lookup the exported symbols, list them and offer these
0359  * for commands for generation
0360  * 
0361  * @param p_cmd_map
0362  * @param argc
0363  * @param argv
0364  * @return SUCCEED
0365  */
0366 expublic int cmd_gen(cmd_mapping_t *p_cmd_map, int argc, char **argv, int *p_have_next)
0367 {
0368     int ret=EXSUCCEED;
0369     const PSChar *s;
0370     HPSCRIPTVM v = NULL;
0371     PSInteger res;
0372     int i;
0373     const PSChar *err;
0374     char tmp[PATH_MAX+1];
0375     gen_hash_t *gen;
0376         
0377     FILE *f = NULL;
0378     
0379     v = ps_open(1024); /* creates a VM with initial stack size 1024 */
0380     
0381     ps_setprintfunc(v,printfunc,errorfunc);
0382     
0383     ps_pushroottable(v);
0384     
0385     /* register functions */
0386     psstd_register_bloblib(v);
0387     psstd_register_iolib(v);
0388     psstd_register_systemlib(v);
0389     psstd_register_mathlib(v);
0390     psstd_register_stringlib(v);
0391     psstd_register_exutillib(v);
0392     
0393     register_getExfields(v);
0394     
0395     /* aux library
0396      * sets error handlers */
0397     psstd_seterrorhandlers(v);
0398     
0399     /* Load any parameters in different table */
0400     
0401     ps_pushstring(v, "args", -1); /* 2 */
0402     ps_newtable(v); /* 3 */
0403     
0404     
0405     if (argc>=3)
0406     {
0407         snprintf(tmp, sizeof(tmp), "%s %s", argv[1], argv[2]);
0408         
0409         /* lookup the command */
0410         if (NULL==(gen = gen_get(tmp)))
0411         {
0412             fprintf(stderr, "Unknown gen sub-command: [%s]\n", tmp);
0413             EXFAIL_OUT(ret);
0414         }
0415     }
0416     else 
0417     {
0418         fprintf(stderr, "Invalid arguments, format: gen <language> <type>\n");
0419         EXFAIL_OUT(ret);
0420     }
0421     
0422     /* make full command for defaults lookup */
0423     snprintf(tmp, sizeof(tmp), "gen %s %s", argv[1], argv[2]);
0424     
0425     if (EXSUCCEED!=add_defaults_from_config(v, tmp))
0426     {
0427         EXFAIL_OUT(ret);
0428     }
0429     
0430     if (argc>3)
0431     {
0432         for (i=3; i<argc; i++)
0433         {
0434             /* load defaults */
0435             if (0==strcmp(argv[i], "-d"))
0436             {
0437                 PSBool isDefaulted = EXTRUE;
0438                 ps_pushstring(v, "isDefaulted", -1); /* 4 */
0439                 ps_pushbool(v, isDefaulted);
0440                 ps_newslot(v, -3, PSFalse );/* 3 */
0441             }
0442             else if (0==strncmp(argv[i], "-v", 2) 
0443                     && 0!=strcmp(argv[i], "-v") )
0444             {
0445                 /* pass in value definition (as string) 
0446                  * format <key>=<value>
0447                  */
0448                 if (EXSUCCEED!=load_value(v, argv[i]+2))
0449                 {
0450                     fprintf(stderr, "Invalid value\n");
0451                     EXFAIL_OUT(ret);
0452                 }
0453             }
0454             else if (0==strncmp(argv[i], "-v", 2))
0455             {
0456                 /* next value is key */
0457                 if (i+1<argc)
0458                 {
0459                     i++;
0460                     if (EXSUCCEED!=load_value(v, argv[i]))
0461                     {
0462                         fprintf(stderr, "Invalid value\n");
0463                         EXFAIL_OUT(ret);
0464                     }
0465                 }
0466                 else
0467                 {
0468                     fprintf(stderr, "Invalid command line missing value at end.\n");
0469                     EXFAIL_OUT(ret);
0470                 }
0471             }
0472             
0473         }
0474     }
0475     
0476     /* Load the command line arguments to the script */
0477     
0478     ps_newslot(v, -3, PSFalse );/*1*/
0479 
0480 
0481     /* do some stuff with pscript here */
0482     if (gen->stock)
0483     {
0484         PSMemReader reader;
0485         
0486         memset(&reader, 0, sizeof(reader));
0487         reader.memptr = (char *)gen->stock;
0488         reader.size = gen->stock_len;
0489         
0490         if (PS_FAILED(psstd_loadmem(v, &reader)))
0491         {
0492             fprintf(stderr, "Failed to compile stock script\n");
0493 
0494             if(PS_SUCCEEDED(ps_getstring(v,-1,&err)))
0495             {
0496                 fprintf(stderr, _SC("Error [%s]\n"),err);
0497                 EXFAIL_OUT(ret);
0498             }
0499         }
0500         else
0501         {
0502             ps_pushroottable(v);
0503         }
0504     }
0505     else
0506     {
0507         /* it is file read & compile */
0508         FILE *f=fopen(gen->fname,"rb");
0509         
0510         if(f)
0511         {
0512             if (PS_FAILED(ps_compile(v,file_lexfeedASCII,f,gen->fname,1)))
0513             {
0514                fprintf(stderr, "Failed to compile stock script\n");
0515 
0516                if(PS_SUCCEEDED(ps_getstring(v,-1,&err)))
0517                {
0518                    fprintf(stderr, _SC("Error [%s]\n"),err);
0519                    EXFAIL_OUT(ret);
0520                }
0521             }
0522             else
0523             {
0524                 ps_pushroottable(v);
0525             } 
0526         }
0527         else
0528         {
0529             fprintf(stderr, "Failed to read script file: [%s]:%s", 
0530                     gen->fname, strerror(errno));
0531             EXFAIL_OUT(ret);
0532         }
0533     }
0534 
0535     /* Load the script & run globals... */
0536     if (PS_FAILED(ps_call(v,1,PSTrue, PSTrue)))
0537     {
0538         fprintf(stderr, "Failed to load script\n");
0539         ps_getlasterror(v);
0540         if(PS_SUCCEEDED(ps_getstring(v,-1,&err)))
0541         {
0542             printf(_SC("Error [%s]\n"),err);
0543             EXFAIL_OUT(ret);
0544         }
0545     }
0546     
0547     /* pop the result out of the stack */
0548     ps_getinteger(v,-1,&res);
0549     
0550     if (res>=0)
0551     {
0552         ret = EXSUCCEED;
0553     }
0554     else
0555     {
0556         ret = EXFAIL;
0557     }
0558     
0559     ps_pop(v,3); /* pops the roottable and the function */
0560 
0561 out:
0562     if (v)
0563     {
0564         ps_close(v);
0565     }
0566 
0567     if (f)
0568     {
0569         fclose(f);
0570     }
0571     
0572 
0573     return ret;
0574 }
0575 
0576 #else
0577 /**
0578  * Pscript not supported
0579  * @return FAIL
0580  */
0581 expublic int cmd_gen_help(void)
0582 {
0583     fprintf(stderr, "Pscript not compiled for this platform!\n");
0584     return EXFAIL;
0585 }
0586 
0587 /**
0588  * Not compiled on this platform
0589  * @param p_cmd_map
0590  * @param argc
0591  * @param argv
0592  * @return FAIL
0593  */
0594 expublic int cmd_gen(cmd_mapping_t *p_cmd_map, int argc, char **argv, int *p_have_next)
0595 {
0596     fprintf(stderr, "Pscript not compiled for this platform!\n");
0597     return EXFAIL;
0598 }
0599 
0600 /**
0601  * Dummy for non pscript systems
0602  * @return SUCCEED
0603  */
0604 expublic int cmd_gen_load_scripts(void)
0605 {
0606     return EXSUCCEED;
0607 }
0608 
0609 #endif
0610 /* vim: set ts=4 sw=4 et smartindent: */