diff --git a/Event_Providers.md b/Event_Providers.md index 5f1efcb..3938264 100644 --- a/Event_Providers.md +++ b/Event_Providers.md @@ -1,59 +1,64 @@ * List of Event Providers and associated GUID: -| Provider Name | Provider GUID | File Name | -|:--------------------------------------------------:|:--------------------------------------:|:-----------------:| -| MicrosoftWindowsFileExplorer | {8E12DCD2-FE15-5AF4-2A6A-E707D9DC7DE5} | Explorer.exe | -| Microsoft.Notepad (old) | {30D0A2A5-808D-567B-18FE-2AE44C127BDC} | Notepad.exe | -| Microsoft.Notepad (new) | {E29EB67A-714D-4D58-A598-46DEE87E620B} | Notepad.exe | -| MICROSOFT_TWINAPI_PUBLISHER | {5F0E257F-C224-43E5-9555-2ADCB8540A58} | Explorer.exe | -| Microsoft.Web.Platform | {FF32ADA1-5A4B-583C-889E-A3C027B201F5} | UrlMon.dll | -| Microsoft.Windows.AppLifeCycle.UI | {EE97CDC4-B095-5C70-6E37-A541EB74C2B5} | Explorer.exe | -| Microsoft.Windows.Base.Win32.Job | {58E1853A-3C4E-4BBA-9FF8-E1CD088D25A5} | Kernel32.dll | -| Microsoft.Windows.CleanupMgr | {CE790967-FF23-464C-A976-1389674E3972} | CleanMgr.exe | -| Microsoft.Windows.Console.Host | {FE1FF234-1F09-50A8-D38D-C44FAB43E818} | ConHost.exe | -| Microsoft.Windows.Console.Launcher | {770AA552-671A-5E97-579B-151709EC0DBD} | ConHost.exe | -| Microsoft.Windows.Console.Render.VtEngine | {C9BA2A95-D3CA-5E19-2BD6-776A0910CB9D} | ConHost.exe | -| Microsoft.Windows.Console.VirtualTerminal.Parser | {C9BA2A84-D3CA-5E19-2BD6-776A0910CB9D} | ConHost.exe | -| Microsoft.Windows.ContentDeliveryManager | {8CBA0F81-8AD7-5395-2125-5703822C822A} | Explorer.exe | -| Microsoft.Windows.Desktop.Shell.ImmersiveIcons | {A51097AD-C000-5EA3-BBD4-863ADDAEDD23} | Explorer.exe | -| Microsoft.Windows.Desktop.Shell.NotificationArea | {653FE5BD-E1D2-5D40-D93C-A551A97CD49A} | Explorer.exe | -| Microsoft.Windows.Desktop.Shell.OOBEHealth | {397B9505-A6BA-5951-46EE-84B08FB14812} | Explorer.exe | -| Microsoft.Windows.Desktop.Shell.SoftLanding | {9954158F-EAA7-5AFE-B990-DF3CCE23483A} | Explorer.exe | -| Microsoft.Windows.Dwm.DwmApi | {504665A2-31F7-4B2F-BF1B-9635312E8088} | DwmApi.dll | -| Microsoft_Windows_Dwm_Dwm_Provider | {D29D56EA-4867-4221-B02E-CFD998834075} | Dwm.exe | -| Microsoft_Windows_Dwm_Udwm_Provider | {A2D1C713-093B-43A7-B445-D09370EC9F47} | Dwm.exe | -| Microsoft.Windows.Licensing.IUI | {753436F5-735D-41FA-B4B7-D68579AC5582} | Explorer.exe | -| Microsoft.Windows.Lxss.Manager | {B99CDB5A-039C-5046-E672-1A0DE0A40211} | LxssManager.dll | -| Microsoft.Windows.MobilityExperience | {5AFB7971-45E5-4D49-AAEB-1B04D39872CF} | Explorer.exe | -| Microsoft.Windows.NTVDM | {70CAA5B8-A8F0-408A-8B53-563BFF7FF2FF} | Kernel32.dll | -| Microsoft.Windows.PerfLib | {BC44FFCD-964B-5B85-8662-0BA87EDAF07A} | AdvApi32.dll | -| Microsoft.Windows.Shell.CoCreateInstanceAsSystem | {FFE467F7-4F51-4061-82BE-C2ED8946A961} | Explorer.exe | -| Microsoft.Windows.Shell.ControlCenter | {2C00A440-76DE-4FE3-856F-00557535BE83} | Explorer.exe | -| Microsoft.Windows.Shell.Desktop.LogonFramework | {04D28E21-00AA-5228-CFD0-D70863AA5CE9} | Explorer.exe | -| Microsoft.Windows.Shell.Explorer | {5F1E1B94-A9FE-57D8-ABE7-D29A6DF9E967} | Explorer.exe | -| Microsoft.Windows.Shell.NotificationCenter | {4BFE0FDE-99D6-5630-8A47-DA7BFAEFD876} | Explorer.exe | -| Microsoft.Windows.Shell.PrivacyConsentLogging | {58B09B7D-FD44-5A27-101D-5D2472A7BB42} | Explorer.exe | -| Microsoft.Windows.Shell.ScalingCompat | {2DBD0B99-C886-5C44-9FC2-7220DDF5AAF6} | DwmApi.dll | -| Microsoft.Windows.Shell.StateCapture | {82A0F3C6-C4DC-54FB-F358-354C5026DC61} | Explorer.exe | -| Microsoft.Windows.Shell.Taskbar | {DF8DAB3F-B1C9-58D3-2EA1-4C08592BB71B} | Explorer.exe | -| Microsoft.Windows.Shell.TileBadgeProvider | {34D3FCA3-41F2-4498-B7A0-58708572B583} | Explorer.exe | -| Microsoft.Windows.ShellExperienceDispatcher | {273C19B2-6643-5A58-6288-C336D3688B8D} | Explorer.exe | -| Microsoft.Windows.ShellPlacements | {7CA6A4DD-DAE5-5FB7-EC8E-4A6C648FADF9} | Explorer.exe | -| Microsoft_Windows_Shell_Core_Provider | {30336ED4-E327-447C-9DE0-51B652C86108} | Explorer.exe | -| Microsoft-Windows-Shell-CortanaProactive | {0E6F34B3-0637-55AB-F0BB-8B8FA83EDA04} | Explorer.exe | -| Microsoft-Windows-Shell-Launcher | {3D6120A6-0986-51C4-213A-E2975903051D} | Explorer.exe | -| Microsoft.Windows.Security.IsolationApi | {B6FD710B-F783-4B1C-AB9C-C68099DCC0C7} | SecHost.dll | -| Microsoft.Windows.Security.MitigationPolicy | {CA967C75-04BF-40B5-9A16-98B5F9332A92} | SecHost.dll | -| Microsoft.Windows.Subsystem.Adss | {754E4536-6735-4194-BE81-1374BD2E9B0D} | LxCore.sys | -| Microsoft.Windows.Subsystem.LxCore | {0CD1C309-0878-4515-83DB-749843B3F5C9} | LxCore.sys | -| Microsoft.Windows.Subsystem.Lxss | {D90B9468-67F0-5B3B-42CC-82AC81FFD960} | WslHost.exe | -| Microsoft.Windows.Taskmgr | {2E635D8E-1107-4555-9319-32EEB895AAAE} | TaskMgr.exe | -| Microsoft-Windows-UAC | {E7558269-3FA5-46ED-9F4D-3C6E282DDE55} | Kernel32.dll | -| Microsoft.Windows.Wil.FeatureLogging | {DCEF5411-1F98-5EE7-238B-5ABD0E078E97} | Explorer.exe | -| MSNT_SystemTrace | {9E814AAD-3204-11D2-9A82-006008A86939} | | -| TelemetryAssert | {6D1B249D-131B-468A-899B-FB0AD9551772} | Explorer.exe | -| TelemetryAssertDiagTrack | {E0B47CF8-E776-4EA7-9EC0-93A85B9A7A2B} | Explorer.exe | -| WERSVC_TRIGGER_PROVIDER_GUID | {E46EEAD8-0C54-4489-9898-8FA79D059E0E} | Dwm.exe | +| Provider Name | Provider GUID | File Name | +|:--------------------------------------------------:|:--------------------------------------:|:-------------------:| +| MicrosoftWindowsFileExplorer | {8E12DCD2-FE15-5AF4-2A6A-E707D9DC7DE5} | Explorer.exe | +| Microsoft.Notepad (old) | {30D0A2A5-808D-567B-18FE-2AE44C127BDC} | Notepad.exe | +| Microsoft.Notepad (new) | {E29EB67A-714D-4D58-A598-46DEE87E620B} | Notepad.exe | +| MICROSOFT_TWINAPI_PUBLISHER | {5F0E257F-C224-43E5-9555-2ADCB8540A58} | Explorer.exe | +| Microsoft.Web.Platform | {FF32ADA1-5A4B-583C-889E-A3C027B201F5} | UrlMon.dll | +| Microsoft.Windows.AppLifeCycle.UI | {EE97CDC4-B095-5C70-6E37-A541EB74C2B5} | Explorer.exe | +| Microsoft.Windows.Base.Win32.Job | {58E1853A-3C4E-4BBA-9FF8-E1CD088D25A5} | Kernel32.dll | +| Microsoft.Windows.CleanupMgr | {CE790967-FF23-464C-A976-1389674E3972} | CleanMgr.exe | +| Microsoft.Windows.Console.Host | {FE1FF234-1F09-50A8-D38D-C44FAB43E818} | ConHost.exe | +| Microsoft.Windows.Console.Launcher | {770AA552-671A-5E97-579B-151709EC0DBD} | ConHost.exe | +| Microsoft.Windows.Console.Render.VtEngine | {C9BA2A95-D3CA-5E19-2BD6-776A0910CB9D} | ConHost.exe | +| Microsoft.Windows.Console.VirtualTerminal.Parser | {C9BA2A84-D3CA-5E19-2BD6-776A0910CB9D} | ConHost.exe | +| Microsoft.Windows.ContentDeliveryManager | {8CBA0F81-8AD7-5395-2125-5703822C822A} | Explorer.exe | +| Microsoft.Windows.Desktop.Shell.ImmersiveIcons | {A51097AD-C000-5EA3-BBD4-863ADDAEDD23} | Explorer.exe | +| Microsoft.Windows.Desktop.Shell.NotificationArea | {653FE5BD-E1D2-5D40-D93C-A551A97CD49A} | Explorer.exe | +| Microsoft.Windows.Desktop.Shell.OOBEHealth | {397B9505-A6BA-5951-46EE-84B08FB14812} | Explorer.exe | +| Microsoft.Windows.Desktop.Shell.SoftLanding | {9954158F-EAA7-5AFE-B990-DF3CCE23483A} | Explorer.exe | +| Microsoft.Windows.Dwm.DwmApi | {504665A2-31F7-4B2F-BF1B-9635312E8088} | DwmApi.dll | +| Microsoft_Windows_Dwm_Dwm_Provider | {D29D56EA-4867-4221-B02E-CFD998834075} | Dwm.exe | +| Microsoft_Windows_Dwm_Udwm_Provider | {A2D1C713-093B-43A7-B445-D09370EC9F47} | Dwm.exe | +| Microsoft.Windows.FaultReporting | {1377561D-9312-452C-AD13-C4A1C9C906E0} | WerFault.exe | +| Microsoft.Windows.HangReporting | {3E0D88DE-AE5C-438A-BB1C-C2E627F8AECB} | WerSvc.dll | +| Microsoft.Windows.Licensing.IUI | {753436F5-735D-41FA-B4B7-D68579AC5582} | Explorer.exe | +| Microsoft.Windows.Lxss.Manager | {B99CDB5A-039C-5046-E672-1A0DE0A40211} | LxssManager.dll | +| Microsoft.Windows.MobilityExperience | {5AFB7971-45E5-4D49-AAEB-1B04D39872CF} | Explorer.exe | +| Microsoft.Windows.NTVDM | {70CAA5B8-A8F0-408A-8B53-563BFF7FF2FF} | Kernel32.dll | +| Microsoft.Windows.PerfLib | {BC44FFCD-964B-5B85-8662-0BA87EDAF07A} | AdvApi32.dll | +| Microsoft.Windows.Shell.CoCreateInstanceAsSystem | {FFE467F7-4F51-4061-82BE-C2ED8946A961} | Explorer.exe | +| Microsoft.Windows.Shell.ControlCenter | {2C00A440-76DE-4FE3-856F-00557535BE83} | Explorer.exe | +| Microsoft.Windows.Shell.Desktop.LogonFramework | {04D28E21-00AA-5228-CFD0-D70863AA5CE9} | Explorer.exe | +| Microsoft.Windows.Shell.Explorer | {5F1E1B94-A9FE-57D8-ABE7-D29A6DF9E967} | Explorer.exe | +| Microsoft.Windows.Shell.NotificationCenter | {4BFE0FDE-99D6-5630-8A47-DA7BFAEFD876} | Explorer.exe | +| Microsoft.Windows.Shell.PrivacyConsentLogging | {58B09B7D-FD44-5A27-101D-5D2472A7BB42} | Explorer.exe | +| Microsoft.Windows.Shell.ScalingCompat | {2DBD0B99-C886-5C44-9FC2-7220DDF5AAF6} | DwmApi.dll | +| Microsoft.Windows.Shell.StateCapture | {82A0F3C6-C4DC-54FB-F358-354C5026DC61} | Explorer.exe | +| Microsoft.Windows.Shell.Taskbar | {DF8DAB3F-B1C9-58D3-2EA1-4C08592BB71B} | Explorer.exe | +| Microsoft.Windows.Shell.TileBadgeProvider | {34D3FCA3-41F2-4498-B7A0-58708572B583} | Explorer.exe | +| Microsoft.Windows.ShellExperienceDispatcher | {273C19B2-6643-5A58-6288-C336D3688B8D} | Explorer.exe | +| Microsoft.Windows.ShellPlacements | {7CA6A4DD-DAE5-5FB7-EC8E-4A6C648FADF9} | Explorer.exe | +| Microsoft_Windows_Shell_Core_Provider | {30336ED4-E327-447C-9DE0-51B652C86108} | Explorer.exe | +| Microsoft-Windows-Shell-CortanaProactive | {0E6F34B3-0637-55AB-F0BB-8B8FA83EDA04} | Explorer.exe | +| Microsoft-Windows-Shell-Launcher | {3D6120A6-0986-51C4-213A-E2975903051D} | Explorer.exe | +| Microsoft.Windows.Security.IsolationApi | {B6FD710B-F783-4B1C-AB9C-C68099DCC0C7} | SecHost.dll | +| Microsoft.Windows.Security.MitigationPolicy | {CA967C75-04BF-40B5-9A16-98B5F9332A92} | SecHost.dll | +| Microsoft.Windows.Subsystem.Adss | {754E4536-6735-4194-BE81-1374BD2E9B0D} | LxCore.sys | +| Microsoft.Windows.Subsystem.LxCore | {0CD1C309-0878-4515-83DB-749843B3F5C9} | LxCore.sys | +| Microsoft.Windows.Subsystem.Lxss | {D90B9468-67F0-5B3B-42CC-82AC81FFD960} | WslHost.exe | +| Microsoft.Windows.Taskmgr | {2E635D8E-1107-4555-9319-32EEB895AAAE} | TaskMgr.exe | +| Microsoft-Windows-UAC | {E7558269-3FA5-46ED-9F4D-3C6E282DDE55} | Kernel32.dll | +| Microsoft.Windows.Wil.FeatureLogging | {DCEF5411-1F98-5EE7-238B-5ABD0E078E97} | Explorer.exe | +| Microsoft.Windows.WindowsErrorReporting | {CC79CF77-70D9-4082-9B52-23F3A3E92FE4} | WerFault.exe | +| Microsoft.Windows.WERSecureVertical | {97945555-B04C-47C0-B399-E453D509A5F0} | WerFaultSecure.exe | +| Microsoft.Windows.WERVertical | {2B87E57E-7BD0-43A3-A278-02E62D59B2B1} | WerFault.exe | +| MSNT_SystemTrace | {9E814AAD-3204-11D2-9A82-006008A86939} | | +| TelemetryAssert | {6D1B249D-131B-468A-899B-FB0AD9551772} | Explorer.exe | +| TelemetryAssertDiagTrack | {E0B47CF8-E776-4EA7-9EC0-93A85B9A7A2B} | Explorer.exe | +| WERSVC_TRIGGER_PROVIDER_GUID | {E46EEAD8-0C54-4489-9898-8FA79D059E0E} | Dwm.exe | * List of Event Providers from `SecHost!EtwpGuidMap` (array of GUIDs): diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad4d0d5 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +#Root Makefile for TraceEvent project + +exe: + cd src ; $(MAKE) diff --git a/Others/getopt.c b/Others/getopt.c new file mode 100644 index 0000000..bc35f96 --- /dev/null +++ b/Others/getopt.c @@ -0,0 +1,535 @@ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +#include +#include +#include +#define __INSIDE_CYGWIN__ +#include + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/Others/getopt.h b/Others/getopt.h new file mode 100644 index 0000000..1922a0e --- /dev/null +++ b/Others/getopt.h @@ -0,0 +1,95 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int optind; /* index of first non-option in argv */ +extern int optopt; /* single option character, as parsed */ +extern int opterr; /* flag to enable built-in diagnostics... */ + /* (user may set to zero, to suppress) */ + +extern char *optarg; /* pointer to argument of current option */ + +extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +extern int getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); +extern int getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ diff --git a/README.md b/README.md index 71977b8..60d73d4 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,63 @@ # TraceEvent -Trace events in real time sessions with documented Windows API. The undocumented section will be added in [NtTrace repository](https://github.com/Biswa96/NtTrace.git). +[![Licence](https://img.shields.io/github/license/Biswa96/TraceEvent.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0.en.html) +[![Top Language](https://img.shields.io/github/languages/top/Biswa96/TraceEvent.svg?style=for-the-badge)](https://github.com/Biswa96/TraceEvent.git) +[![Code size](https://img.shields.io/github/languages/code-size/Biswa96/TraceEvent.svg?style=for-the-badge)]() + +Trace events in real time sessions with documented Windows API. The undocumented section will be added in second version. ## What is Event Tracing See this Microsoft Documentation: [Event Tracing][1] +## How to build + +Clone this repository. Open the solution (.sln) or project (.vcxproj) file in Visual Studio and build it. Alternatively, run Visual Studio developer command prompt, go to the cloned folder and run this command: `msbuild.exe /p:Configuration=Debug`. You can also build with mingw-w64 toolchain. Go to the folder in terminal run `make` command for mingw-w64/msys2/cygwin. + ## How to use Download the executable from [Release Page][2]. Run this program as administrator every time. Here are the options. +``` +Usage: TraceEvent.exe [--] [option] [argument] +Options: + -g, --guid Add Event Provider GUID with trace session. + -L, --list List all trace sessions. + -l, --log Log events in real time. + -q, --query Query status of trace session. + -S, --start Starts the trace session. + -s, --stop Stops the trace session. + -h, --help Display usage information. +``` + ### Start a session -Run this command as administrator: `TraceEvent.exe start `. Always use an unique session name otherwise this will show error. Event provider GUIDs can be found from this Powershell cmdlet: `Get-EtwTraceProvider`. Also the [TraceLog tool][3] from Windows Driver Kit provides a list of those GUIDs and the required command: `tracelog -enumguid`. **Always use curly brackets** to specify GUID strings. Find more GUIDs in [**Event Providers list**](Event_Providers.md). For example: `TraceEvent.exe start MyTrace {12345678-1234-1234-1234-123457890ABCD}` +Run this command as administrator: `TraceEvent.exe --start --guid `. Always use an unique session name otherwise this will show error. Event provider GUIDs can be found from this Powershell cmdlet: `Get-EtwTraceProvider`. Also the [TraceLog tool][3] from Windows Driver Kit provides a list of those GUIDs and the required command: `tracelog -enumguid`. **Always use curly brackets** to specify GUID strings. Find more GUIDs in [**Event Providers list**](Event_Providers.md). For example: `TraceEvent.exe --start MyTrace --guid {12345678-1234-1234-1234-123457890ABCD}` -### Log events from a session +### Log events -Run this command as administrator: `TraceEvent.exe log `. Only use session names which are started previously. If CPU usage becomes high redirect output to a file to logs events, e.g. `TraceEvent.exe log MyTrace > FileName.txt`. +Run this command as administrator: `TraceEvent.exe --log `. Only use session names which are started previously. If CPU usage becomes high then redirect output to a file. e.g. `TraceEvent.exe --log MyTrace > FileName.txt` ### Stop a session -Run this command as administrator: `TraceEvent.exe stop `. Stop only the previously opened tracing session. Using an already stopped session will show error. For example user this command to stop previously opened 'MyTrace' session: `TraceEvent.exe stop MyTrace`. - -## How to build - -Clone this repository with `git clone https://github.com/Biswa96/TraceEvent.git`. Open the solution file TraceEvent.sln in visual Studio and build it. Or use MSBuild: `MSBuild.exe TraceEvent.sln /p:Configuration=Release`. +Run this command as administrator: `TraceEvent.exe --stop `. Stop only the previously opened tracing session. Using an already stopped session will show error. For example user this command to stop previously opened 'MyTrace' session: `TraceEvent.exe --stop MyTrace`. -makefile will be added soon. +## Project Overview -## Known issues +Here are the overview of source files according to their dependencies: -* There are some logging format issue with the Event Providers from Explorer.exe. To view those use TraceLog.exe or TraceFmt.exe from Windows Driver Kit. Use event providers from Explorer.exe carefully. +``` +TraceEvent\ + | + +-- Functions: Helper functions to Log status and convert GUID to string + +-- PrintProperties: Display Event session details and it's security properties + +-- CallBacks: Callback functions to log events messages + +-- TraceEvent: Functions to start, stop, log and other tasks + | + | +-- wgetopt: Converted from Cygwin getopt file for wide characters + | | + +-- main: Main function with option processing +``` ## Further Readings @@ -43,8 +70,7 @@ makefile will be added soon. This project is licensed under [GPLv3+](LICENSE). This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. ``` -TraceEvent - Trace and Log Events in real time mode -Copyright (C) Biswapriyo Nath +TraceEvent -- (c) Copyright 2018 Biswapriyo Nath This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/TraceEvent.sln b/TraceEvent.sln index 5028251..d309a5a 100644 --- a/TraceEvent.sln +++ b/TraceEvent.sln @@ -8,13 +8,10 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 - Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DD4E40A0-0A41-4A62-A426-F4BDF5C11296}.Debug|x64.ActiveCfg = Debug|x64 {DD4E40A0-0A41-4A62-A426-F4BDF5C11296}.Debug|x64.Build.0 = Debug|x64 - {DD4E40A0-0A41-4A62-A426-F4BDF5C11296}.Release|x64.ActiveCfg = Release|x64 - {DD4E40A0-0A41-4A62-A426-F4BDF5C11296}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TraceEvent.vcxproj b/TraceEvent.vcxproj index 6a895a0..c4142e0 100644 --- a/TraceEvent.vcxproj +++ b/TraceEvent.vcxproj @@ -5,10 +5,6 @@ Debug x64 - - Release - x64 - @@ -16,18 +12,21 @@ + + + 15.0 {DD4E40A0-0A41-4A62-A426-F4BDF5C11296} TraceEvent - 10.0.17134.0 + 10.0.17763.0 @@ -36,13 +35,6 @@ v141 MultiByte - - Application - false - v141 - true - MultiByte - @@ -51,39 +43,20 @@ - - - - + + $(SolutionDir)bin\ + $(SolutionDir)bin\ + Level4 Disabled - true - true - FastCall - MultiThreadedDebug - - - ntdll.lib;tdh.lib;%(AdditionalDependencies) - - - - - Level4 - MinSpace - true - true - true - true - MultiThreaded - FastCall + _CRT_SECURE_NO_WARNINGS + 4201 - true - true - ntdll.lib;tdh.lib;%(AdditionalDependencies) + Advapi32.lib;ntdll.lib;tdh.lib;shell32.lib;kernel32.lib diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..7054fa3 --- /dev/null +++ b/build.bat @@ -0,0 +1,28 @@ +@echo off +::Set Environments for X86_64 build +cd %~dp0 +call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +where cl.exe link.exe + +::Set Environment Variables +set NAME=TraceEvent +set BINDIR=bin +set SRCDIR=src +set CFLAGS=/c /nologo /O1 /MD /W4 /Fo%BINDIR%\\ +set LFLAGS=/nologo /MACHINE:X64 +set LIBS=Advapi32.lib Ntdll.lib Tdh.lib Shell32.lib + +::Other compiler options +set CCOPT=/D_CRT_SECURE_NO_WARNINGS /wd"4201" + +::Build +rd /s /q %BINDIR% +mkdir %BINDIR% +cl.exe %CFLAGS% %CCOPT% %SRCDIR%\*.c +link.exe %LFLAGS% %LIBS% %BINDIR%\*.obj /OUT:%BINDIR%\%NAME%.exe + +dir /B %BINDIR%\*.exe +pause +exit /b + +::END# \ No newline at end of file diff --git a/desktop.ini b/desktop.ini deleted file mode 100644 index d957fd1..0000000 --- a/desktop.ini +++ /dev/null @@ -1,4 +0,0 @@ -[ViewState] -Mode= -Vid= -FolderType=Generic diff --git a/src/CallBacks.c b/src/CallBacks.c index 612b4c2..1c00e9e 100644 --- a/src/CallBacks.c +++ b/src/CallBacks.c @@ -1,123 +1,126 @@ -#include "CallBacks.h" +#include +#include #include -void RemoveTrailingSpace(PEVENT_MAP_INFO mapInfo) +#define LODWORD(x) ((DWORD)(x)) +#define HIDWORD(x) ((DWORD)(((x) >> 32) & 0xffffffff)) + +void RemoveTrailingSpace( + PEVENT_MAP_INFO EventMapInfo) { size_t ByteLength = 0; - for (DWORD i = 0; i < mapInfo->EntryCount; i++) + for (DWORD i = 0; i < EventMapInfo->EntryCount; i++) { - ByteLength = (wcslen((PWCHAR)((PBYTE)mapInfo + mapInfo->MapEntryArray[i].OutputOffset)) - 1) * 2; - *((PWCHAR)((PBYTE)mapInfo + (mapInfo->MapEntryArray[i].OutputOffset + ByteLength))) = L'\0'; + ByteLength = (wcslen((PWCHAR)((PBYTE)EventMapInfo + EventMapInfo->MapEntryArray[i].OutputOffset)) - 1) * sizeof(wchar_t); + *((PWCHAR)((PBYTE)EventMapInfo + (EventMapInfo->MapEntryArray[i].OutputOffset + ByteLength))) = L'\0'; } } void GetMapInfo( - _In_ PEVENT_RECORD eRecord, - _In_ PWCHAR MapName, - _In_ ULONG DecodingSource, - _Out_ PEVENT_MAP_INFO mapInfo) + PEVENT_RECORD EventRecord, + PWCHAR MapName, + ULONG DecodingSource, + PEVENT_MAP_INFO EventMapInfo) { - ULONG result, size = 0; - - result = TdhGetEventMapInformation(eRecord, MapName, mapInfo, &size); - mapInfo = (PEVENT_MAP_INFO)malloc(size); - result = TdhGetEventMapInformation(eRecord, MapName, mapInfo, &size); + ULONG MapSize = 0; + ULONG result = TdhGetEventMapInformation(EventRecord, MapName, EventMapInfo, &MapSize); + EventMapInfo = malloc(MapSize); + result = TdhGetEventMapInformation(EventRecord, MapName, EventMapInfo, &MapSize); - if (result == 0) + if (result == ERROR_SUCCESS) { if (DecodingSource == DecodingSourceXMLFile) - RemoveTrailingSpace(mapInfo); + RemoveTrailingSpace(EventMapInfo); } } -//Event Providers use EventWriteTransfer function -void EventRecordCallback(PEVENT_RECORD eRecord) +// Event Providers use EventWriteTransfer function +void EventRecordCallback( + struct _EVENT_RECORD* EventRecord) { - ULONG result, size = 0; - PTRACE_EVENT_INFO eInfo = NULL; - PEVENT_MAP_INFO mapInfo = NULL; - - PBYTE EndOfUserData = (PBYTE)eRecord->UserData + eRecord->UserDataLength; - PBYTE UserData = (PBYTE)eRecord->UserData; + PEVENT_MAP_INFO EventMapInfo = NULL; + PBYTE EndOfUserData = (PBYTE)EventRecord->UserData + EventRecord->UserDataLength; + PBYTE UserData = (PBYTE)EventRecord->UserData; ULONG FormattedDataSize = 0; PWCHAR FormattedData = NULL; USHORT UserDataConsumed = 0; - //Verbose Event Header Information + // Verbose Event Header Information FILETIME ft; SYSTEMTIME st; - ft.dwHighDateTime = eRecord->EventHeader.TimeStamp.HighPart; - ft.dwLowDateTime = eRecord->EventHeader.TimeStamp.LowPart; + ft.dwHighDateTime = EventRecord->EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = EventRecord->EventHeader.TimeStamp.LowPart; FileTimeToSystemTime(&ft, &st); wprintf(L"%02d/%02d/%04d-%02d:%02d:%02d.%03d :: " L"ThreadID: %ld ProcessID: %ld\n", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, - eRecord->EventHeader.ThreadId, - eRecord->EventHeader.ProcessId); - - //Get Event Information - result = TdhGetEventInformation(eRecord, 0, NULL, NULL, &size); - eInfo = (PTRACE_EVENT_INFO)malloc(size); - result = TdhGetEventInformation(eRecord, 0, NULL, eInfo, &size); - if (result != 0) + EventRecord->EventHeader.ThreadId, + EventRecord->EventHeader.ProcessId); + + // Get Event Information + ULONG size = 0; + ULONG result = TdhGetEventInformation(EventRecord, 0, NULL, NULL, &size); + PTRACE_EVENT_INFO EventInfo = malloc(size); + result = TdhGetEventInformation(EventRecord, 0, NULL, EventInfo, &size); + if (result != ERROR_SUCCESS) { wprintf(L"TdhGetEventInformation Error: %ld\n", result); return; } - //Print Event Information: Provider Name and Task Name - if (eInfo->ProviderNameOffset > 0) - wprintf(L"[%ls] ", (PWCHAR)((PBYTE)(eInfo) + eInfo->ProviderNameOffset)); - if (eInfo->TaskNameOffset > 0) - wprintf(L"%ls ", (PWCHAR)((PBYTE)(eInfo) + eInfo->TaskNameOffset)); + // Print Event Information: Provider Name and Task Name + if (EventInfo->ProviderNameOffset > 0) + wprintf(L"[%ls] ", (PWCHAR)((PBYTE)(EventInfo) +EventInfo->ProviderNameOffset)); + if (EventInfo->TaskNameOffset > 0) + wprintf(L"%ls ", (PWCHAR)((PBYTE)(EventInfo) +EventInfo->TaskNameOffset)); - //Print Event Property: Property Name and Property Data - if (eInfo->TopLevelPropertyCount > 0) + // Print Event Property: Property Name and Property Data + if (EventInfo->TopLevelPropertyCount > 0) { - for (ULONG i = 0; i < eInfo->TopLevelPropertyCount; i++) + for (ULONG i = 0; i < EventInfo->TopLevelPropertyCount; i++) { wprintf(L"%ls:", - (PWCHAR)((PBYTE)(eInfo) + eInfo->EventPropertyInfoArray[i].NameOffset)); + (PWCHAR)((PBYTE)(EventInfo) +EventInfo->EventPropertyInfoArray[i].NameOffset)); GetMapInfo( - eRecord, - (PWCHAR)((PBYTE)(eInfo) + eInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), - eInfo->DecodingSource, - mapInfo); + EventRecord, + (PWCHAR)((PBYTE)(EventInfo) +EventInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), + EventInfo->DecodingSource, + EventMapInfo); result = TdhFormatProperty( - eInfo, - mapInfo, + EventInfo, + EventMapInfo, sizeof(PVOID), - eInfo->EventPropertyInfoArray[i].nonStructType.InType, - eInfo->EventPropertyInfoArray[i].nonStructType.OutType, - eInfo->EventPropertyInfoArray[i].length, + EventInfo->EventPropertyInfoArray[i].nonStructType.InType, + EventInfo->EventPropertyInfoArray[i].nonStructType.OutType, + EventInfo->EventPropertyInfoArray[i].length, (USHORT)(EndOfUserData - UserData), UserData, &FormattedDataSize, FormattedData, &UserDataConsumed); - FormattedData = (PWCHAR)malloc(FormattedDataSize); + FormattedData = malloc(FormattedDataSize); result = TdhFormatProperty( - eInfo, - mapInfo, + EventInfo, + EventMapInfo, sizeof(PVOID), - eInfo->EventPropertyInfoArray[i].nonStructType.InType, - eInfo->EventPropertyInfoArray[i].nonStructType.OutType, - eInfo->EventPropertyInfoArray[i].length, + EventInfo->EventPropertyInfoArray[i].nonStructType.InType, + EventInfo->EventPropertyInfoArray[i].nonStructType.OutType, + EventInfo->EventPropertyInfoArray[i].length, (USHORT)(EndOfUserData - UserData), UserData, &FormattedDataSize, FormattedData, &UserDataConsumed); - if (result == 0) + if (result == ERROR_SUCCESS) { wprintf(L"%ls ", FormattedData); UserData += UserDataConsumed; @@ -125,23 +128,31 @@ void EventRecordCallback(PEVENT_RECORD eRecord) } } - //Cleanup + // Cleanup wprintf(L"\n"); - free(eInfo); - free(mapInfo); + free(EventInfo); + free(EventMapInfo); free(FormattedData); } -//Print Buffer Statistics after every Event Buffer flushes -ULONG TotalLost = 0; +// Print Buffer Statistics after every Event Buffer flushes +unsigned long TotalLost = 0, BlockNumber = 0; -ULONG BufferCallback(PEVENT_TRACE_LOGFILEW Buffer) +unsigned long BufferCallback( + struct _EVENT_TRACE_LOGFILEW* LogFile) { - TotalLost += Buffer->EventsLost; + SYSTEMTIME st; + FileTimeToSystemTime((PFILETIME)&LogFile->CurrentTime, &st); + + // Increment Counting variables + TotalLost += LogFile->EventsLost; + ++BlockNumber; wprintf( - L"Filled=%8d, Lost=%3d TotalLost= %d\r", - Buffer->Filled, Buffer->EventsLost, TotalLost); + L"\n%02d/%02d/%04d-%02d:%02d:%02d.%03d :: %8d: Filled=%8d, Lost=%3d TotalLost= %d\r", + st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, + BlockNumber, LogFile->Filled, LogFile->EventsLost, TotalLost); return TRUE; } diff --git a/src/CallBacks.h b/src/CallBacks.h index 4e3a3f6..b7de191 100644 --- a/src/CallBacks.h +++ b/src/CallBacks.h @@ -1,10 +1,10 @@ #ifndef CALLBACKS_H #define CALLBACKS_H -#include -#include +void EventRecordCallback( + struct _EVENT_RECORD* eRecord); -void EventRecordCallback(PEVENT_RECORD EventRecord); -ULONG BufferCallback(PEVENT_TRACE_LOGFILEW Buffer); +unsigned long BufferCallback( + struct _EVENT_TRACE_LOGFILEW* Buffer); #endif //CALLBACKS_H diff --git a/src/Functions.c b/src/Functions.c index cc3f1b6..b9db2c0 100644 --- a/src/Functions.c +++ b/src/Functions.c @@ -1,8 +1,10 @@ -#include "Functions.h" +#include #include -//Copied form ReactOS /dll/win32/ole32/compobj.c -static inline BOOL is_valid_hex(WCHAR c) +#define GUID_STRING 128u + +/* Form ReactOS /dll/win32/ole32/compobj.c */ +static inline int is_valid_hex(wchar_t c) { if (!(((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || @@ -11,7 +13,7 @@ static inline BOOL is_valid_hex(WCHAR c) return TRUE; } -static const BYTE guid_conv_table[256] = +static const unsigned char guid_conv_table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ @@ -23,9 +25,11 @@ static const BYTE guid_conv_table[256] = }; /* conversion helper for CLSIDFromString/IIDFromString */ -BOOL guid_from_string(PWCHAR s, GUID* id) +int guid_from_string( + wchar_t* s, + struct _GUID* id) { - int i; + int i; if (!s || s[0] != '{') { memset(id, 0, sizeof(GUID)); @@ -71,10 +75,13 @@ BOOL guid_from_string(PWCHAR s, GUID* id) return FALSE; } -void PrintGuid(GUID* id, PWCHAR string) +void PrintGuid( + struct _GUID* id, + wchar_t* string) { - swprintf( - string, GUID_STRING, + swprintf_s( + string, + GUID_STRING, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", id->Data1, id->Data2, id->Data3, id->Data4[0], id->Data4[1], id->Data4[2], @@ -82,26 +89,46 @@ void PrintGuid(GUID* id, PWCHAR string) id->Data4[6], id->Data4[7]); } -void GetFormattedMessage(ULONG result) +void Log( + unsigned long Result, + wchar_t* Function) { - wchar_t MsgBuffer[MsgSize]; + wchar_t* MsgBuffer = NULL; + unsigned long tChars = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + Result, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (wchar_t*)&MsgBuffer, + 0, + NULL); + + if (tChars) + wprintf(L"%ls%ld\t %ls", Function, (Result & 0xFFFF), MsgBuffer); + else + wprintf(L"%ls%ld\n", Function, (Result & 0xFFFF)); - memset(MsgBuffer, 0, MsgSize); - FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, result, MsgSize, MsgBuffer, MsgSize, NULL); - wprintf(L"Operation Status: %lu\n%ls", result, MsgBuffer); + LocalFree(MsgBuffer); } -void Usage(PWCHAR progm) +void Usage( + void) { wprintf( - L"Usage: %ls [option] [arguments]...\n" + L"\nTraceEvent -- (c) Copyright 2018 Biswapriyo Nath\n" + L"Licensed under GNU Public License version 3 or higher\n\n" L"Trace and log events in real time sessions\n" - L"\n" + L"Usage: TraceEvent.exe [--] [option] [argument]\n\n" L"Options:\n" - L" start Starts the trace session.\n" - L" stop Stops the trace session.\n" - L" log Logs events in real time.\n" - L" help command Displays this usage information.\n", progm); + L" -g, --guid Add Event Provider GUID with trace session.\n" + L" -L, --list List all trace sessions.\n" + L" -l, --log Log events in real time.\n" + L" -q, --query Query status of trace session.\n" + L" -S, --start Starts the trace session.\n" + L" -s, --stop Stops the trace session.\n" + L" -h, --help Display usage information.\n" + L"\n" + ); } diff --git a/src/Functions.h b/src/Functions.h index be91a12..e6b702b 100644 --- a/src/Functions.h +++ b/src/Functions.h @@ -1,14 +1,23 @@ #ifndef FUNCTIONS_H #define FUNCTIONS_H -#include +#include -#define GUID_STRING 40 -#define MsgSize 0x400 +#define GUID_STRING 128u -BOOL guid_from_string(PWCHAR s, GUID* id); -void PrintGuid(GUID* id, PWCHAR string); -void GetFormattedMessage(ULONG result); -void Usage(PWCHAR prog); +int guid_from_string( + wchar_t* s, + struct _GUID* id); + +void PrintGuid( + struct _GUID* id, + wchar_t* string); + +void Log( + unsigned long Result, + wchar_t* Function); + +void Usage( + void); #endif //FUNCTIONS_H diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..2a5fb7e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,25 @@ +#Makefile for TraceEvent project + +NAME := TraceEvent +BINDIR = ../bin +CC = gcc +CFLAGS := -ggdb -m64 -Wall -Wextra +LIBS := -lAdvapi32 -lNtdll -lTdh -lShell32 -lKernel32 +SOURCES := $(wildcard *.c) +OBJECTS := $(SOURCES:.c=.obj) +BINS := $(patsubst %.obj,$(BINDIR)/%.obj,$(OBJECTS)) + +EXECUTABLE=$(BINDIR)/$(NAME) + +all: $(BINDIR) $(EXECUTABLE) + +$(EXECUTABLE): $(BINS) + $(CC) $(CFLAGS) $^ $(LIBS) -o $@ + +$(BINS): $(BINDIR)/%.obj: %.c + $(CC) -c $(CFLAGS) -fstack-check $< -o $(BINDIR)/$*.obj + +$(BINDIR): + @mkdir ..\bin + +#END \ No newline at end of file diff --git a/src/PrintProperties.c b/src/PrintProperties.c index 186721a..2d799db 100644 --- a/src/PrintProperties.c +++ b/src/PrintProperties.c @@ -1,84 +1,67 @@ -#include "PrintProperties.h" +#include "WinInternal.h" #include #include +#include "Functions.h" #define SEC_INFO ( OWNER_SECURITY_INFORMATION \ - | GROUP_SECURITY_INFORMATION \ - | DACL_SECURITY_INFORMATION \ + | GROUP_SECURITY_INFORMATION \ + | DACL_SECURITY_INFORMATION \ | SACL_SECURITY_INFORMATION ) -#define STATUS_BUFFER_TOO_SMALL 0xC0000023L +#define STATUS_BUFFER_TOO_SMALL (long)0xC0000023L -typedef enum _EVENT_TRACE_INFORMATION_CLASS { - EventTraceSessionSecurityInformation = 4 -} EVENT_TRACE_INFORMATION_CLASS; - -typedef struct _EVENT_TRACE_SESSION_SECURITY_INFORMATION { - EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; - ULONG SecurityInformation; - ULONG64 TraceHandle; - SECURITY_DESCRIPTOR SecurityDescriptor[ANYSIZE_ARRAY]; -} EVENT_TRACE_SESSION_SECURITY_INFORMATION, *PEVENT_TRACE_SESSION_SECURITY_INFORMATION; - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemPerformanceTraceInformation = 31 -} SYSTEM_INFORMATION_CLASS; - -NTSTATUS NtQuerySystemInformation( - SYSTEM_INFORMATION_CLASS SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength -); - -ULONG SecurityDescriptorString(ULONG64 TraceHandle) +ULONG SecurityDescriptorString( + TRACEHANDLE TraceHandle) { - ULONG result = 0, ReturnLen = 0; - ULONG InfoLen = sizeof(EVENT_TRACE_SESSION_SECURITY_INFORMATION); - NTSTATUS status; - PWCHAR wstring = NULL; + BOOL bRes = 0; + NTSTATUS Status; + PWSTR StringSecurityDescriptor = NULL; PEVENT_TRACE_SESSION_SECURITY_INFORMATION SessionInfo = NULL; + ULONG ReturnLen, InfoLen = sizeof(*SessionInfo); while (TRUE) { if (SessionInfo) free(SessionInfo); - SessionInfo = (PEVENT_TRACE_SESSION_SECURITY_INFORMATION)malloc(InfoLen); + SessionInfo = malloc(InfoLen); if (!SessionInfo) break; SessionInfo->EventTraceInformationClass = EventTraceSessionSecurityInformation; SessionInfo->TraceHandle = TraceHandle; //Properties->Wnode.HistoricalContext; - SessionInfo->SecurityInformation = DACL_SECURITY_INFORMATION; //0x4 - status = NtQuerySystemInformation( + SessionInfo->SecurityInformation = DACL_SECURITY_INFORMATION; //4u + Status = NtQuerySystemInformation( SystemPerformanceTraceInformation, - SessionInfo, InfoLen, &ReturnLen); + SessionInfo, + InfoLen, + &ReturnLen); InfoLen = ReturnLen; - if (status != STATUS_BUFFER_TOO_SMALL) + if (Status != STATUS_BUFFER_TOO_SMALL) { - if (status >= S_OK) + if (Status >= S_OK) { - result = ConvertSecurityDescriptorToStringSecurityDescriptorW( + bRes = ConvertSecurityDescriptorToStringSecurityDescriptorW( SessionInfo->SecurityDescriptor, SDDL_REVISION_1, SEC_INFO, - &wstring, + &StringSecurityDescriptor, NULL); - if (result == 1) - wprintf(L"Session Security: %ls\n", wstring); + if (bRes) + wprintf(L"Session Security: %ls\n", StringSecurityDescriptor); } + free(SessionInfo); - return result; + return bRes; } } - return result; + return bRes; } -void PrintTraceProperties(PEVENT_TRACE_PROPERTIES Properties) +void PrintTraceProperties( + struct _EVENT_TRACE_PROPERTIES_V2* Properties) { - PWCHAR ClockType, MaximumFileSize, unit; + PWCHAR ClockType; WCHAR Guid[GUID_STRING]; - ULONG FlushTimer; if(Properties->LoggerNameOffset) wprintf(L"Logger Name: %ls\n", (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset)); @@ -109,21 +92,32 @@ void PrintTraceProperties(PEVENT_TRACE_PROPERTIES Properties) wprintf(L"Real Time Buffers Lost: %ld\n", Properties->RealTimeBuffersLost); wprintf(L"Real Time Consumers: %ld\n", Properties->Wnode.ProviderId); - if (Properties->Wnode.ClientContext == 1) + if (Properties->Wnode.ClientContext == EVENT_TRACE_CLOCK_PERFCOUNTER) ClockType = L"ClockType: PerfCounter\n"; - else if (Properties->Wnode.ClientContext == 2) + else if (Properties->Wnode.ClientContext == EVENT_TRACE_CLOCK_SYSTEMTIME) ClockType = L"ClockType: SystemTime\n"; else { ClockType = L"ClockType CPU Cycle\n"; - if (Properties->Wnode.ClientContext != 3) + if (Properties->Wnode.ClientContext != EVENT_TRACE_CLOCK_CPUCYCLE) ClockType = L"ClockType: Unknown\n"; } wprintf(ClockType); + if (Properties->LogFileMode & EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING) + wprintf(L"No per-processor buffering\n"); + + if (Properties->LogFileMode & (EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN | EVENT_TRACE_PERSIST_ON_HYBRID_SHUTDOWN)) + { + PWSTR Shutdown = L"Persist"; + if (Properties->LogFileMode & EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN) + Shutdown = L"Stop"; + wprintf(L"Hybrid Shutdown: %ls\n", Shutdown); + } + if (Properties->MaximumFileSize) { - MaximumFileSize = L"Maximum File Size: %ld Kb\n"; + PWSTR MaximumFileSize = L"Maximum File Size: %ld Kb\n"; if (!(Properties->LogFileMode & EVENT_TRACE_USE_KBYTES_FOR_SIZE)) MaximumFileSize = L"Maximum File Size: %ld Mb\n"; wprintf(MaximumFileSize, Properties->MaximumFileSize); @@ -131,20 +125,17 @@ void PrintTraceProperties(PEVENT_TRACE_PROPERTIES Properties) else wprintf(L"Maximum File Size: not set\n"); - FlushTimer = Properties->FlushTimer; - if (FlushTimer) + if (Properties->FlushTimer) { - unit = L"secs"; + PWSTR unit = L"secs"; if ((Properties->LogFileMode >> 4) & 1) unit = L"ms"; - wprintf(L"Buffer Flush Timer: %ld %ls\n", FlushTimer, unit); + wprintf(L"Buffer Flush Timer: %ld %ls\n", Properties->FlushTimer, unit); } else wprintf(L"Buffer Flush Timer: not set\n"); if (Properties->LogFileNameOffset > 0) - { - wprintf(L"Log Filename: %ls\n", - (PWCHAR)((PBYTE)Properties + Properties->LogFileNameOffset)); - } + wprintf(L"Log Filename: %ls\n", (PWCHAR)((PBYTE)Properties + Properties->LogFileNameOffset)); + wprintf(L"\n"); } diff --git a/src/PrintProperties.h b/src/PrintProperties.h index a63c83e..ca429d8 100644 --- a/src/PrintProperties.h +++ b/src/PrintProperties.h @@ -1,9 +1,7 @@ #ifndef PRINTPROPERTIES_H #define PRINTPROPERTIES_H -#include "Functions.h" -#include - -void PrintTraceProperties(PEVENT_TRACE_PROPERTIES Properties); +void PrintTraceProperties( + struct _EVENT_TRACE_PROPERTIES_V2* Properties); #endif //PRINTPROPERTIES_H diff --git a/src/TraceEvent.c b/src/TraceEvent.c index e518f5a..c567672 100644 --- a/src/TraceEvent.c +++ b/src/TraceEvent.c @@ -1,105 +1,159 @@ -#include "TraceEvent.h" +#include +#include +#include "CallBacks.h" +#include "PrintProperties.h" +#include "Functions.h" #include -#define MAX_LOGGER_SIZE 0x400 -static const ULONG BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + MAX_LOGGER_SIZE * sizeof(WCHAR); +#define EtwpMaxLoggers 64 +#define MAX_LOGGER_SIZE (1024 * sizeof(wchar_t)) +#define MAX_LOGFILE_SIZE (1024 * sizeof(wchar_t)) -ULONG StartSession(PWCHAR LoggerName, GUID ProviderID) -{ - ULONG result; - TRACEHANDLE hTrace; - WCHAR Guid[GUID_STRING]; +static const size_t BufferSize = sizeof(EVENT_TRACE_PROPERTIES_V2) + MAX_LOGGER_SIZE; - PEVENT_TRACE_PROPERTIES Properties = (PEVENT_TRACE_PROPERTIES)malloc(BufferSize); +unsigned long StartSession( + wchar_t* LoggerName, + struct _GUID* ProviderID) +{ + PEVENT_TRACE_PROPERTIES_V2 Properties = malloc(BufferSize); memset(Properties, 0, BufferSize); - Properties->Wnode.BufferSize = BufferSize; - Properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + Properties->Wnode.BufferSize = (ULONG)BufferSize; + Properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_VERSIONED_PROPERTIES; Properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; - Properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + Properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES_V2); + + TRACEHANDLE TraceHandle = 0; + ULONG result = StartTraceW( + &TraceHandle, + LoggerName, + (PEVENT_TRACE_PROPERTIES)Properties); - result = StartTraceW(&hTrace, LoggerName, Properties); - if (result) + if (result == ERROR_SUCCESS) { - wprintf( - L"Could not start \"%ls\" to logger\n", - (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset)); - GetFormattedMessage(result); + wprintf(L"Logger Started...\nEnabling \"%ls\" to logger %I64d\n", + (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset), TraceHandle); + Log(result, L"StartTraceW Status: "); } else { - wprintf(L"Logger Started...\nEnabling \"%ls\" to logger %I64d\n", - (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset), hTrace); - GetFormattedMessage(result); + wprintf( + L"Could not start \"%ls\" to logger\n", + (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset)); + Log(result, L"StartTraceW Status: "); } result = EnableTraceEx2( - hTrace, &ProviderID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, + TraceHandle, ProviderID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, NULL); - if (result) + if (result == ERROR_SUCCESS) { - wprintf(L"ERROR: Failed to enable Guid: "); - PrintGuid(&ProviderID, Guid); - wprintf(L"%ls\n", &Guid); + wprintf(L"Enabled logger...\n"); + PrintTraceProperties(Properties); } else { - wprintf(L"Enabled logger...\n"); - PrintTraceProperties(Properties); + WCHAR Guid[GUID_STRING]; + wprintf(L"ERROR: Failed to enable Guid: "); + PrintGuid(ProviderID, Guid); + wprintf(L"%ls\n", &Guid); } free(Properties); return result; } -ULONG ConsumeEvent(PWCHAR LoggerName) +unsigned long ConsumeEvent( + wchar_t* LoggerName) { - ULONG result; - TRACEHANDLE hTrace; - EVENT_TRACE_LOGFILEW LogFile = { 0 }; LogFile.LoggerName = LoggerName; LogFile.EventRecordCallback = (PEVENT_RECORD_CALLBACK)EventRecordCallback; LogFile.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACKW)BufferCallback; - LogFile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING; + LogFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; - hTrace = OpenTraceW(&LogFile); - if (hTrace == (TRACEHANDLE)INVALID_HANDLE_VALUE) + TRACEHANDLE TraceHandle = OpenTraceW(&LogFile); + if (TraceHandle == (TRACEHANDLE)INVALID_HANDLE_VALUE) { - result = GetLastError(); - wprintf(L"OpenTrace Error: %lu\n", result); - GetFormattedMessage(result); + Log(GetLastError(), L"OpenTraceW Status: "); return 0; } - result = ProcessTrace(&hTrace, 1, 0, 0); - if (result) - { - wprintf(L"ProcessTrace Error: %lu\n", result); - GetFormattedMessage(result); - } + ULONG result = ProcessTrace(&TraceHandle, 1, NULL, NULL); + if (result != ERROR_SUCCESS) + Log(result, L"ProcessTrace Status: "); return result; } -ULONG StopSession(PWCHAR LoggerName) +unsigned long StopSession( + wchar_t* LoggerName) { - PEVENT_TRACE_PROPERTIES Properties = (PEVENT_TRACE_PROPERTIES)malloc(BufferSize); + PEVENT_TRACE_PROPERTIES_V2 Properties = malloc(BufferSize); memset(Properties, 0, BufferSize); - Properties->Wnode.BufferSize = BufferSize; + Properties->Wnode.BufferSize = (ULONG)BufferSize; + Properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_VERSIONED_PROPERTIES; + + ULONG result = ControlTraceW( + (TRACEHANDLE)NULL, + LoggerName, + (PEVENT_TRACE_PROPERTIES)Properties, + EVENT_TRACE_CONTROL_STOP); - ULONG result = ControlTraceW((TRACEHANDLE)NULL, LoggerName, Properties, EVENT_TRACE_CONTROL_STOP); - if (result == 0) + Log(result, L"ControlTraceW Status: "); + if (result == ERROR_SUCCESS) { wprintf(L"\"%ls\" session stopped succesfully\n", (PWCHAR)((PBYTE)Properties + Properties->LoggerNameOffset)); PrintTraceProperties(Properties); } - else - { - wprintf(L"ControlTraceW Error: %ld\n", result); - GetFormattedMessage(result); - } free(Properties); return result; } + +unsigned long QuerySession( + wchar_t* LoggerName) +{ + PEVENT_TRACE_PROPERTIES_V2 Properties = malloc(BufferSize); + memset(Properties, 0, BufferSize); + Properties->Wnode.BufferSize = (ULONG)BufferSize; + Properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_VERSIONED_PROPERTIES; + + ULONG result = ControlTraceW( + (TRACEHANDLE)NULL, + LoggerName, + (PEVENT_TRACE_PROPERTIES)Properties, + EVENT_TRACE_CONTROL_QUERY); + + Log(result, L"ControlTraceW Status: "); + if (result == ERROR_SUCCESS) + PrintTraceProperties(Properties); + + free(Properties); + return result; +} + +void ListSessions( + void) +{ + ULONG result = 0; + PEVENT_TRACE_PROPERTIES_V2 Properties = malloc(sizeof(*Properties)); + + for (int i = 0; i < EtwpMaxLoggers; i++) + { + memset(Properties, 0, sizeof(*Properties)); + Properties->Wnode.BufferSize = (ULONG)sizeof(*Properties); + + result = ControlTraceW( + (TRACEHANDLE)i, + NULL, + (PEVENT_TRACE_PROPERTIES)Properties, + EVENT_TRACE_CONTROL_QUERY); + + if (!result || result == ERROR_MORE_DATA) + PrintTraceProperties(Properties); + i++; + }; + + free(Properties); +} diff --git a/src/TraceEvent.h b/src/TraceEvent.h index bf0a74d..bffdc03 100644 --- a/src/TraceEvent.h +++ b/src/TraceEvent.h @@ -1,11 +1,22 @@ #ifndef TRACEVENT_H #define TRACEVENT_H -#include "CallBacks.h" -#include "PrintProperties.h" +#include -ULONG StartSession(PWCHAR LoggerName, GUID ProviderID); -ULONG ConsumeEvent(PWCHAR LoggerName); -ULONG StopSession(PWCHAR LoggerName); +unsigned long StartSession( + wchar_t* LoggerName, + struct _GUID* ProviderID); + +unsigned long ConsumeEvent( + wchar_t* LoggerName); + +unsigned long StopSession( + wchar_t* LoggerName); + +unsigned long QuerySession( + wchar_t* LoggerName); + +void ListSessions( + void); #endif //TRACEVENT_H diff --git a/src/WinInternal.h b/src/WinInternal.h new file mode 100644 index 0000000..cc4182f --- /dev/null +++ b/src/WinInternal.h @@ -0,0 +1,78 @@ +#ifndef WININTERNAL_H +#define WININTERNAL_H + +#include +#include + +#define EVENT_TRACE_CLOCK_RAW 0 +#define EVENT_TRACE_CLOCK_PERFCOUNTER 1 +#define EVENT_TRACE_CLOCK_SYSTEMTIME 2 +#define EVENT_TRACE_CLOCK_CPUCYCLE 3 + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _UNICODE_STRING64 { + USHORT Length; + USHORT MaximumLength; + ULONGLONG Buffer; +} UNICODE_STRING64, *PUNICODE_STRING64; + +/* From ProcessHacker phnt/include/ntexapi.h */ +typedef enum _EVENT_TRACE_INFORMATION_CLASS { + EventTraceKernelVersionInformation, + EventTraceGroupMaskInformation, + EventTracePerformanceInformation, + EventTraceTimeProfileInformation, + EventTraceSessionSecurityInformation, + EventTraceSpinlockInformation, + EventTraceStackTracingInformation, + EventTraceExecutiveResourceInformation, + EventTraceHeapTracingInformation, + EventTraceHeapSummaryTracingInformation, + EventTracePoolTagFilterInformation, + EventTracePebsTracingInformation, + EventTraceProfileConfigInformation, + EventTraceProfileSourceListInformation, + EventTraceProfileEventListInformation, + EventTraceProfileCounterListInformation, + EventTraceStackCachingInformation, + EventTraceObjectTypeFilterInformation, + EventTraceSoftRestartInformation, + EventTraceLastBranchConfigurationInformation, + EventTraceLastBranchEventListInformation, + EventTraceProfileSourceAddInformation, + EventTraceProfileSourceRemoveInformation, + EventTraceProcessorTraceConfigurationInformation, + EventTraceProcessorTraceEventListInformation, + EventTraceCoverageSamplerInformation, + MaxEventTraceInfoClass +} EVENT_TRACE_INFORMATION_CLASS; + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemPerformanceTraceInformation = 31 +} SYSTEM_INFORMATION_CLASS; + +typedef struct _EVENT_TRACE_SESSION_SECURITY_INFORMATION { + EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; + ULONG SecurityInformation; + ULONG64 TraceHandle; + SECURITY_DESCRIPTOR SecurityDescriptor[1]; +} EVENT_TRACE_SESSION_SECURITY_INFORMATION, *PEVENT_TRACE_SESSION_SECURITY_INFORMATION; + +NTSTATUS NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _In_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_ PULONG ReturnLength); + +void RtlSetLastWin32Error( + _In_ ULONG LastError); + +ULONG RtlNtStatusToDosError( + _In_ NTSTATUS Status); + +#endif // WININTERNAL_H diff --git a/src/main.c b/src/main.c index a81dd03..2f5660b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,47 +1,93 @@ -#define _CRT_SECURE_NO_WARNINGS +#include +#include "Functions.h" #include "TraceEvent.h" #include +#include "wgetopt.h" int main(void) { int wargc; - PWCHAR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + PWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); - if (wargc < 3) + if (wargc < 2) { wprintf( L"Provide a valid option and agrument.\n" - L"Try \"TraceEvent.exe help command\" for more information.\n"); + L"Try 'TraceEvent.exe --help' for more information.\n"); return 0; } - if (!wcscmp(L"start", wargv[1])) - { - GUID ProviderID; + int c, bRes; + GUID ProviderID = { 0 }; + BOOLEAN Start = FALSE; + PWSTR LoggerName = NULL; - if (guid_from_string(wargv[3], &ProviderID)) - StartSession(wargv[2], ProviderID); - else - wprintf(L"Enter Provider GUID correctly\n"); + /* Option Table */ + const struct option OptionTable[] = { + { L"guid", required_argument, 0, 'g' }, + { L"help", no_argument, 0, 'h' }, + { L"list", no_argument, 0, 'L' }, + { L"log", required_argument, 0, 'l' }, + { L"query", required_argument, 0, 'q' }, + { L"start", required_argument, 0, 'S' }, + { L"stop", required_argument, 0, 's' }, + { 0, no_argument, 0, 0 }, + }; - return 0; - } - else if (!wcscmp(L"log", wargv[1])) - { - ConsumeEvent(wargv[2]); - } - else if (!wcscmp(L"stop", wargv[1])) - { - StopSession(wargv[2]); - } - else if (!wcscmp(L"help", wargv[1])) + /* Option parsing */ + while ((c = wgetopt_long(wargc, wargv, L"g:hLl:q:S:s:", OptionTable, 0)) != -1) { - Usage(wargv[0]); + switch (c) + { + case 0: + wprintf(L"Try 'TraceEvent.exe --help' for more information.\n"); + Usage(); + break; + + case 'g': + bRes = guid_from_string(optarg, &ProviderID); + if (!bRes) + { + ProviderID = (GUID){ 0 }; + wprintf(L"Enter Provider GUID correctly\n"); + } + break; + + case 'L': + ListSessions(); + break; + + case 'l': + ConsumeEvent(optarg); + break; + + case 'q': + QuerySession(optarg); + break; + + case 'S': + LoggerName = optarg; + Start = TRUE; + break; + + case 's': + StopSession(optarg); + break; + + case 'h': + Usage(); + break; + + default: + wprintf(L"Try 'TraceEvent.exe --help' for more information.\n"); + } } - else + + if (Start) { - wprintf( - L"Not a valid option or argument.\n" - L"Try \"TraceEvent.exe help command\" for more information.\n"); + if (ProviderID.Data1 && ProviderID.Data4) + StartSession(LoggerName, &ProviderID); + else + wprintf(L"Event Provider GUID is not added, use '--guid' option to add.\n"); } } diff --git a/src/wgetopt.c b/src/wgetopt.c new file mode 100644 index 0000000..b82899d --- /dev/null +++ b/src/wgetopt.c @@ -0,0 +1,535 @@ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +#include +#include +#include +#define __INSIDE_CYGWIN__ +#include "wgetopt.h" + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +wchar_t *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifdef __CYGWIN__ +static wchar_t EMSG[] = (wchar_t*)L""; +#else +#define EMSG (wchar_t*)L"" +#endif + +static int wgetopt_internal(int, wchar_t * const *, const wchar_t *, + const struct option *, int *, int); +static int parse_long_options(wchar_t * const *, const wchar_t *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, wchar_t * const *); + +static wchar_t *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const wchar_t recargchar[] = L"option requires an argument -- %c\n"; +static const wchar_t recargstring[] = L"option requires an argument -- %s\n"; +static const wchar_t ambig[] = L"ambiguous option -- %.*s\n"; +static const wchar_t noarg[] = L"option doesn't take an argument -- %.*s\n"; +static const wchar_t illoptchar[] = L"unknown option -- %c\n"; +static const wchar_t illoptstring[] = L"unknown option -- %s\n"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + wchar_t * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + wchar_t *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((wchar_t **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((wchar_t **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx, int short_too) +{ + wchar_t *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = wcschr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = wcslen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (wcsncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (wcslen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + fwprintf(stderr, ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + fwprintf(stderr, noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + fwprintf(stderr, recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + fwprintf(stderr, illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +wgetopt_internal(int nargc, wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx, int flags) +{ + wchar_t *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && wcschr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && wcschr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (wchar_t*)wcschr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + fwprintf(stderr, illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fwprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fwprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +wgetopt(int nargc, wchar_t * const *nargv, const wchar_t *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (wgetopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +wgetopt_long(int nargc, wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx) +{ + + return (wgetopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +wgetopt_long_only(int nargc, wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx) +{ + + return (wgetopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/src/wgetopt.h b/src/wgetopt.h new file mode 100644 index 0000000..4ca47ef --- /dev/null +++ b/src/wgetopt.h @@ -0,0 +1,95 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int optind; /* index of first non-option in argv */ +extern int optopt; /* single option character, as parsed */ +extern int opterr; /* flag to enable built-in diagnostics... */ + /* (user may set to zero, to suppress) */ + +extern wchar_t *optarg; /* pointer to argument of current option */ + +extern int wgetopt(int nargc, wchar_t * const *nargv, const wchar_t *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const wchar_t *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +extern int wgetopt_long(int nargc, wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx); +extern int wgetopt_long_only(int nargc, wchar_t * const *nargv, const wchar_t *options, + const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */