Commit 392937ff authored by Kirk Webb's avatar Kirk Webb

Revamped a bunch of stuff around handling of sysprep.

I realized that the way things were setup was rather confusing.  These changes
make the user a more active participant in handling the unattended setup file.

Also squashed a debug printf and disabled the WMP network service during
Windows configuration.  Lastly, zapped the KMS setup key from the defaults
file and cleaned up the makefile a bit.

Don't go into Audit mode during initial setup anymore.
parent 4ecd72e2
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
# #
# #
# XXX ONLY RUN THIS INSTALL ON A CYGWIN / WINDOWS XP NODE!
# Similar to linux9, cygwinseven is an overlay on linux, which is an overlay # Similar to linux9, cygwinseven is an overlay on linux, which is an overlay
# on common. # on common.
# #
...@@ -63,10 +62,10 @@ SYSPREP = $(SYSTEM32)/sysprep ...@@ -63,10 +62,10 @@ SYSPREP = $(SYSTEM32)/sysprep
SPSCRIPTS = $(WINDOWS)/Setup/Scripts SPSCRIPTS = $(WINDOWS)/Setup/Scripts
install client-install: baselinux-install common-install etc-install \ install client-install: baselinux-install common-install etc-install \
sup-install script-install ##bin-install sup-install script-install bin-install
@echo "Remember to install the PEM files if necessary" @echo "Remember to install the PEM files if necessary"
simple-install: common-install script-install ##bin-install simple-install: common-install script-install
dir-install: dir-install:
...@@ -81,21 +80,10 @@ common-install: dir-install ...@@ -81,21 +80,10 @@ common-install: dir-install
sup-install: dir-install sup-install: dir-install
bin-install: dir-install bin-install: dir-install
# These are found in the /share/windows directory.
$(INSTALL) -m 755 $(SRCDIR)/WSName.exe $(BINDIR)/WSName.exe
$(INSTALL) -m 755 $(SRCDIR)/addusers.exe $(BINDIR)/addusers.exe
$(INSTALL) -m 755 $(SRCDIR)/usrtogrp.exe $(BINDIR)/usrtogrp.exe
# Setx is run under CMD early in the user login to make HOME for Emacs.
$(INSTALL) -m 755 $(SRCDIR)/setx.exe $(SYSTEM32)/setx.exe
# Devcon is used by rc.cygwin and rc.ifc to disable/enable NIC's.
$(INSTALL) -m 755 $(SRCDIR)/devcon.exe $(BINDIR)/devcon.exe
# These are used by tmcd/findif.
$(INSTALL) -m 755 $(SYSTEM32)/getmac.exe $(BINDIR)/getmac.exe
$(INSTALL) -m 755 $(WBEM)/framedyn.dll $(BINDIR)/framedyn.dll
etc-install: dir-install sysetc-install etc-install: dir-install sysetc-install
sysetc-install: dir-install ###ifcfgs sysetc-install: dir-install
# Watchdog assumes the ssh keys are in /etc/ssh. # Watchdog assumes the ssh keys are in /etc/ssh.
mkdir -p /etc/ssh mkdir -p /etc/ssh
ln -f -s /etc/ssh* /etc/ssh ln -f -s /etc/ssh* /etc/ssh
...@@ -110,24 +98,17 @@ script-install: dir-install $(SCRIPTS) ...@@ -110,24 +98,17 @@ script-install: dir-install $(SCRIPTS)
$(INSTALL) -m 755 $(SRCDIR)/prepare $(BINDIR)/prepare $(INSTALL) -m 755 $(SRCDIR)/prepare $(BINDIR)/prepare
$(INSTALL) -m 755 $(SRCDIR)/tbshutdown $(BINDIR)/tbshutdown $(INSTALL) -m 755 $(SRCDIR)/tbshutdown $(BINDIR)/tbshutdown
$(INSTALL) -c -m 755 -o root -d /sbin $(INSTALL) -c -m 755 -o root -d /sbin
#$(INSTALL) -m 755 $(SRCDIR)/reboot /sbin/reboot
# Put the tcsh desktop startup script in the CygWin root directory.
#$(INSTALL) -m 755 $(SRCDIR)/cygwin-tcsh.bat /
ln -f -s /bin/shutdown /sbin/shutdown ln -f -s /bin/shutdown /sbin/shutdown
ln -f -s /bin/reboot /sbin/reboot ln -f -s /bin/reboot /sbin/reboot
# Everybody is in the Administrators group already, so sudo is irrelevant.
$(INSTALL) -m 755 $(SRCDIR)/sudo.dummy /bin/sudo
#$(INSTALL) -m 755 $(SRCDIR)/rc.firstboot $(BINDIR)/rc/rc.firstboot
$(INSTALL) -m 755 $(SRCDIR)/rc.cygwinseven $(BINDIR)/rc/rc.cygwin $(INSTALL) -m 755 $(SRCDIR)/rc.cygwinseven $(BINDIR)/rc/rc.cygwin
#$(INSTALL) -m 755 $(SRCDIR)/clean_logonui $(BINDIR)/clean_logonui
$(INSTALL) -m 755 $(SRCDIR)/rc.reboot $(BINDIR)/rc/rc.reboot $(INSTALL) -m 755 $(SRCDIR)/rc.reboot $(BINDIR)/rc/rc.reboot
$(INSTALL) -m 755 $(SRCDIR)/rc.lmhosts $(BINDIR)/rc/rc.lmhosts $(INSTALL) -m 755 $(SRCDIR)/rc.lmhosts $(BINDIR)/rc/rc.lmhosts
$(INSTALL) -m 755 $(SRCDIR)/netbt $(BINDIR)/netbt $(INSTALL) -m 755 $(SRCDIR)/netbt $(BINDIR)/netbt
$(INSTALL) -m 755 unattend-x86.xml $(SYSPREP)/unattend-src.xml
$(INSTALL) -c -m 755 -o Administrators -d $(SPSCRIPTS) $(INSTALL) -c -m 755 -o Administrators -d $(SPSCRIPTS)
$(INSTALL) -m 755 $(SRCDIR)/setupnode.ps1 $(SPSCRIPTS)/setupnode.ps1 $(INSTALL) -m 755 $(SRCDIR)/setupnode.ps1 $(SPSCRIPTS)/setupnode.ps1
$(INSTALL) -m 755 $(SRCDIR)/SetupComplete.cmd $(SPSCRIPTS)/SetupComplete.cmd $(INSTALL) -m 755 $(SRCDIR)/SetupComplete.cmd $(SPSCRIPTS)/SetupComplete.cmd
$(INSTALL) -m 755 $(SRCDIR)/reboot.sh /bin/reboot $(INSTALL) -m 755 $(SRCDIR)/reboot.sh /bin/reboot
$(INSTALL) -m 755 $(SRCDIR)/hookservices $(BINDIR)/hookservices
sfs-install: sfs-install:
......
...@@ -47,6 +47,9 @@ log Disabling SSDP and UPnP services ...@@ -47,6 +47,9 @@ log Disabling SSDP and UPnP services
runcmd sc.exe ;; config SSDPSRV start= disabled ;; 0 runcmd sc.exe ;; config SSDPSRV start= disabled ;; 0
runcmd sc.exe ;; config upnphost start= disabled ;; 0 runcmd sc.exe ;; config upnphost start= disabled ;; 0
log Disabling WMP network service
runcmd sc.exe ;; config WMPNetworkSvc start= disabled ;; 0
log Disabling Windows Error Reporting log Disabling Windows Error Reporting
addreg HKLM\Software\Microsoft\Windows\Windows Error Reporting ;; Disabled ;; Dword ;; 1 addreg HKLM\Software\Microsoft\Windows\Windows Error Reporting ;; Disabled ;; Dword ;; 1
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
use English;
use Win32;
use Win32API::Net qw(:User);
use Term::ReadKey;
# Turn off line buffering on output
$| = 1;
# Drag in path stuff so we can find emulab stuff.
BEGIN {
require "/etc/emulab/paths.pm";
import emulabpaths;
$ENV{'PATH'} .= ":/cygdrive/c/Windows/System32:/cygdrive/c/Windows";
}
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use libsetup;
use librc;
# Global vars / constants
my $ULEVEL = 1;
sub modpasswd($$)
{
my($user, $pswd) = @_;
my %uh = ();
my $error;
if (!UserGetInfo("", $user, $ULEVEL, \%uh)) {
my $err = Win32::GetLastError();
warning("UserGetInfo failed for $user: $err\n");
return 0;
}
if ($pswd) {
$uh{password} = $pswd;
if (!UserSetInfo("", $user, $ULEVEL, \%uh, \$error)) {
my $err = Win32::GetLastError();
warning("UserSetInfo failed: $err, $error\n");
return 0;
}
}
return 1;
}
sub usage() {
print "$0 [-p <root_password>]\n".
"\n(re)hook the Emulab Windows services.\n";
exit 1;
}
my $optlist = "p:";
my $rootpwd = "";
my $rootpwd2 = "";
# Parse command line.
use Getopt::Std;
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{'p'})) {
$rootpwd = $rootpwd2 = $options{'p'};
}
#
# Need the current root password to setup the Emulab services to match.
# (May already have one from a -p argument. Prompt if not.)
#
while ($rootpwd eq "") {
print "Enter the root password: ";
ReadMode 'noecho';
$rootpwd = ReadLine 0;
chomp $rootpwd;
ReadMode 'normal';
print "\n";
}
while ($rootpwd2 eq "" || $rootpwd ne $rootpwd2) {
print "Didn't match, try again.\n"
if $rootpwd2 ne "" && $rootpwd ne $rootpwd2;
print "Re-enter the root password: ";
ReadMode 'noecho';
$rootpwd2 = ReadLine 0;
chomp $rootpwd2;
ReadMode 'normal';
print "\n";
}
# Set the root password to make sure it matches. We also set this to be
# the Administrator password, to match the sysprep.inf file entry below.
print "\nSetting root password.\n";
modpasswd("root", $rootpwd) or die "Couldn't set root's password!";
#
# Windows stores the password as part of the definition of services that
# run as a real user, such as root, rather than as SYSTEM. Redefine them
# with the current password. Otherwise, Windows will refuse to start them
# up at reboot.
#
my $mybash = "/bin/bash";
print "\nRedefining Pubsub service.\n";
my $pubsub = "/usr/local/libexec/pubsubd";
my $pubsublog = "/var/log/pubsub.log";
system("cygrunsrv -R pubsub > /dev/null 2>&1");
# pubsub runs as the 'SYSTEM' user.
system("cygrunsrv.exe -I pubsub -d 'CYGWIN pubsub' -t auto -y tcpip ".
"-n -p $pubsub -a \"-d -l '$pubsublog'\"");
system("cygrunsrv -VQ pubsub");
my $tbshutdown = "/usr/local/etc/emulab/tbshutdown";
print "\nRedefining EmulabShutdown.\n";
system("cygrunsrv -R EmulabShutdown > /dev/null 2>&1");
system("cygrunsrv -I EmulabShutdown -u root -w '$rootpwd'" .
" --shutdown --type manual -p $mybash " .
" -a \"--norc --noprofile -c '$tbshutdown'\"");
system("cygrunsrv -VQ EmulabShutdown");
print "\nRedefining EmulabStartup.\n";
my $bootsetup = "/usr/local/etc/emulab/rc/rc.bootsetup";
my $progrun = "cygrunsrv -S ProgAgent";
my $bootlog = "/var/log/bootsetup.log";
system("cygrunsrv -R EmulabStartup > /dev/null 2>&1");
system("cygrunsrv -I EmulabStartup -u root -w '$rootpwd'" .
" --dep DHCP --dep iphlpsvc --dep pubsub --dep W32Time" .
" -p $mybash -a \"--norc --noprofile -c " .
" '( $bootsetup; $progrun ) >& $bootlog'\"");
system("cygrunsrv -VQ EmulabStartup");
system("sc config EmulabStartup start= delayed-auto");
exit 0;
...@@ -69,12 +69,11 @@ ...@@ -69,12 +69,11 @@
void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, int); void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, int);
--- serverloop.c.orig 2011-05-20 03:02:50.000000000 -0600 --- serverloop.c.orig 2011-05-20 03:02:50.000000000 -0600
+++ serverloop.c 2012-07-12 17:03:09.583524300 -0600 +++ serverloop.c 2012-07-12 17:03:09.583524300 -0600
@@ -912,6 +912,10 @@ @@ -912,6 +912,9 @@
buffer_append(&stdin_buffer, data, data_len); buffer_append(&stdin_buffer, data, data_len);
memset(data, 0, data_len); memset(data, 0, data_len);
xfree(data); xfree(data);
+#ifdef HAVE_CYGWIN +#ifdef HAVE_CYGWIN
+ logit("woot");
+ input_occurred(); + input_occurred();
+#endif +#endif
} }
......
...@@ -48,43 +48,19 @@ use librc; ...@@ -48,43 +48,19 @@ use librc;
sub usage() sub usage()
{ {
print("Usage: " . scriptname() . " [-p Rootpwd] [-n] [-s] [-c] [-d]\n" . print("Usage: " . scriptname() . " [-p <root_passwd>] [-n] [-s <unattend_file>]\n" .
" -p Rootpwd : Root password, for Emulab services redef.\n" . " -p <root_password> : Root password, for Emulab services redef.\n" .
" If not present, and not -n, will be prompted-for.\n" . " If not present, and not -n, will be prompted-for.\n" .
" -n : Root password was not changed, don't redef services.\n" . " -n : Root password was not changed, don't redef services.\n" .
" -s : Make a hardware-independent image using Sysprep.\n"); " -s <unattend_file> : Make a hardware-independent image using Sysprep.\n");
exit(1); exit(1);
} }
sub modpasswd($$) my $optlist = "p:s:n";
{
my($login, $pswd) = @_;
my %uh = ();
my $error;
if (!UserGetInfo("", $login, $ULEVEL, \%uh)) {
my $err = Win32::GetLastError();
warning("UserGetInfo failed: $err\n");
return 0;
}
if ($pswd) {
$uh{password} = $pswd;
if (!UserSetInfo("", $login, $ULEVEL, \%uh, \$error)) {
my $err = Win32::GetLastError();
warning("UserSetInfo failed: $err, $error\n");
return 0;
}
}
return 1;
}
my $optlist = "p:ns";
my $rootpwd = ""; my $rootpwd = "";
my $rootpwd2 = ""; my $rootpwd2 = "";
my $noredef = 0; my $noredef = 0;
my $sysprep = 0; my $unattend = "";
# Parse command line. # Parse command line.
use Getopt::Std; use Getopt::Std;
...@@ -98,7 +74,8 @@ if (defined($options{'n'})) { ...@@ -98,7 +74,8 @@ if (defined($options{'n'})) {
$noredef = 1; $noredef = 1;
} }
if (defined($options{'s'})) { if (defined($options{'s'})) {
$sysprep = 1; $unattend = $options{'s'};
-f $unattend or die ("$unattend does not exist or is not a file.");
} }
# Only root. No need for this under windows. # Only root. No need for this under windows.
...@@ -144,77 +121,10 @@ my $windir = "/cygdrive/c/WINDOWS"; ...@@ -144,77 +121,10 @@ my $windir = "/cygdrive/c/WINDOWS";
my $sysdir = "$windir/system32"; my $sysdir = "$windir/system32";
my $netcmd = "$sysdir/net.exe"; my $netcmd = "$sysdir/net.exe";
# Could use /share/windows/sysprep, but prepare may have been run once
# already, and unmounted /share. We mount Z: to authenticate, so use that.
my $sysprep_src = "/cygdrive/z/windows/sysprep";
my $sysprep_dst = "/cygdrive/c/sysprep";
my $sysprep_cmd = "$sysprep_dst/sysprep.exe";
if (! $noredef) { if (! $noredef) {
# my $cmd = "$BINDIR/hookservices";
# Need the current root password to re-define the Emulab services to match. $cmd .= $rootpwd ? " -p $rootpwd" : "";
# (May already have one from a -p argument. Prompt if not.) system($cmd) && die "Hook services script failed!";
#
use Term::ReadKey;
while ($rootpwd eq "") {
print "Enter the root password: ";
ReadMode 'noecho';
$rootpwd = ReadLine 0;
chomp $rootpwd;
ReadMode 'normal';
print "\n";
}
while ($rootpwd2 eq "" || $rootpwd ne $rootpwd2) {
print "Didn't match, try again.\n"
if $rootpwd2 ne "" && $rootpwd ne $rootpwd2;
print "Re-enter the root password: ";
ReadMode 'noecho';
$rootpwd2 = ReadLine 0;
chomp $rootpwd2;
ReadMode 'normal';
print "\n";
}
# Set the root password to make sure it matches. We also set this to be
# the Administrator password, to match the sysprep.inf file entry below.
print "\nSetting root password.\n";
modpasswd("root", $rootpwd);
#
# Windows stores the password as part of the definition of services that
# run as a real user, such as root, rather than as SYSTEM. Redefine them
# with the current password. Otherwise, Windows will refuse to start them
# up at reboot.
#
my $mybash = "/bin/bash";
print "\nRedefining Pubsub service.\n";
# Have to remove a service before you can redefine it.
my $pubsub = "/usr/local/libexec/pubsubd";
my $pubsublog = "/var/log/pubsub.log";
system("cygrunsrv -R pubsub");
# pubsub runs as the 'SYSTEM' user.
system("cygrunsrv.exe -I pubsub -d 'CYGWIN pubsub' -t auto -y tcpip ".
"-n -p $pubsub -a \"-d -l '$pubsublog'\"");
system("cygrunsrv -VQ pubsub");
my $tbshutdown = "/usr/local/etc/emulab/tbshutdown";
print "\nRedefining EmulabShutdown.\n";
system("cygrunsrv -R EmulabShutdown");
system("cygrunsrv -I EmulabShutdown -u root -w '$rootpwd'" .
" --shutdown --type manual -p $mybash " .
" -a \"--norc --noprofile -c '$tbshutdown'\"");
system("cygrunsrv -VQ EmulabShutdown");
print "\nRedefining EmulabStartup.\n";
my $bootsetup = "/usr/local/etc/emulab/rc/rc.bootsetup";
my $progrun = "cygrunsrv -S ProgAgent";
my $bootlog = "/var/log/bootsetup.log";
system("cygrunsrv -R EmulabStartup");
system("cygrunsrv -I EmulabStartup -u root -w '$rootpwd'" .
" --dep DHCP --dep iphlpsvc --dep pubsub --dep W32Time" .
" -p $mybash -a \"--norc --noprofile -c " .
" '( $bootsetup; $progrun ) >& $bootlog'\"");
system("cygrunsrv -VQ EmulabStartup");
system("sc config EmulabStartup start= delayed-auto");
} }
# Capture the node name before cleaning out the config files. # Capture the node name before cleaning out the config files.
...@@ -415,51 +325,15 @@ print "\nRe-enabling all network interfaces.\n"; ...@@ -415,51 +325,15 @@ print "\nRe-enabling all network interfaces.\n";
system("devcon enable =net '*PCI*'"); system("devcon enable =net '*PCI*'");
# #
# Run sysprep. # Run sysprep if requested.
# #
if ($sysprep) { if ($unattend) {
my $sp_path = "/cygdrive/c/Windows/System32/sysprep"; my $sp_path = "/cygdrive/c/Windows/System32/sysprep";
# Redefine root password in unattend.xml if necessary
# Also switch reseal mode to 'OOBE' to ensure we don't stay
# in Audit mode.
if ($rootpwd) {
print "Modifying root password in unattend.xml\n";
open(UNANEW, ">$sp_path/unattend-new.xml")
or die "Can't open new unattend file. Sysprep aborted.";
open(UNAORIG, "<$sp_path/unattend-src.xml")
or die "can't open existing unattend.xml. Sysprep aborted.";
while (my $uline = <UNAORIG>) {
my $outline = "";
UNATT1: foreach($uline) {
/<Password>/ && do {
my $dummy = <UNAORIG>, <UNAORIG>; # zap next two lines.
$outline = $uline;
$outline .= " "x28 . "<Value>$rootpwd</Value>\r\n" .
" "x28 . "<PlainText>true</PlainText>\r\n";
last UNATT1;
};
/<Mode>/ && do {
$outline = " "x16 . "<Mode>OOBE</Mode>\r\n";
last UNATT1;
};
# Default
$outline = $uline;
}
print UNANEW $outline;
}
close(UNANEW);
close(UNAORIG);
rename("$sp_path/unattend-new.xml", "$sp_path/unattend.xml") or
die "Can't move new unattend.xml file into place. sysprep aborted."
} else {
print "Not modifying root password in unattend.xml - assuming it's the same or modified outside of this script...\n";
}
# Clean out sysprep work/log areas # Clean out sysprep work/log areas
system("rm -rf /cygdrive/c/Windows/Panther/*"); system("rm -rf /cygdrive/c/Windows/Panther/*");
system("rm -rf $sp_path/Panther/*"); system("rm -rf $sp_path/Panther/*");
system("cp -f $unattend $sp_path/unattend.xml");
chdir $sp_path; chdir $sp_path;
print "\nExecuting sysprep. The system will shutdown shortly. Wait a bit before trying to capture an image (ping the host - wait a minute after it stops).\n"; print "\nExecuting sysprep. The system will shutdown shortly. Wait a bit before trying to capture an image (ping the host - wait a minute after it stops).\n";
system("./sysprep.exe /quiet /oobe /generalize /shutdown /unattend:unattend.xml"); system("./sysprep.exe /quiet /oobe /generalize /shutdown /unattend:unattend.xml");
......
...@@ -34,11 +34,6 @@ ...@@ -34,11 +34,6 @@
<UILanguageFallback>en-US</UILanguageFallback> <UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale> <UserLocale>en-US</UserLocale>
</component> </component>
<component name="Microsoft-Windows-Deployment" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Reseal>
<Mode>Audit</Mode>
</Reseal>
</component>
</settings> </settings>
<settings pass="specialize"> <settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
......
...@@ -144,4 +144,4 @@ WINDOWS_TIMEZONE="Mountain Standard Time" ...@@ -144,4 +144,4 @@ WINDOWS_TIMEZONE="Mountain Standard Time"
# #
# Windows License Keys # Windows License Keys
# #
WINDOWS_KEY_7PRO_X86=FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4 WINDOWS_KEY_7PRO_X86=""
...@@ -16,3 +16,4 @@ THISHOMEBASE=Kwebb.Emulab.Net ...@@ -16,3 +16,4 @@ THISHOMEBASE=Kwebb.Emulab.Net
ELVIN_COMPAT=0 ELVIN_COMPAT=0
PROTOGENI_SUPPORT=0 PROTOGENI_SUPPORT=0
WINDOWS_KEY_7PRO_X86=FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment