Thank you for coming to my talk on Pivotroot for BSD. I just heard of you this morning that this is my second talk in a BSD conference only. The first one was seven years ago and this happens to be the second part of that talk. In between I was working on Ramdisk file systems, small systems, did a number of tutorials and also investigated NECRAF on free BSD. I've worked a lot on free BSD and I have a business that also manages free BSD systems for my customers. And recently about a year and a half ago, I then became a member of the Net BSD Foundation because I actually started moving over and looked at some of their code and it was very... I learned a lot from it and then I figured, okay, let's do something more in that area. So it's actually quite nice to compare and I urge everybody to try to do this. And I've also given a tutorial in comparing the two in Japan but I noticed that the interest was not that high. It was a bit strange. The Net BSD people wanted to see the Net BSD stuff and the free BSD people wanted to see the free BSD stuff but the comparison was not what the people were expecting. So here I'll be showing you the route which I've implemented in Net BSD but I plan to also port it to free BSD. So it's a very simple structure of my presentation. First I'll tell you what problem it solves, what it is actually. And then I will present, I'll show you how it solves it and then I will talk about the implementation. So I've noticed... I've been talking about Pivot Route for ages because it was the end of the other talk I gave and people keep asking why you're going to do this, why you're going to do this every now and then and I said, well, there's not that much interest. But I noticed also that there was also misconceptions so I'm going to tell you what it isn't first just so you understand the difference. And then I'll show you how it runs and then of course I'll show you on the command line. So most people say, oh well it's just truth, isn't it? But we have that. Well it is something like truth but it's not just for one process, it's for the whole system. So truth obviously puts a process into a different route and from there on all the sub-processes are in that route. But it's not that. Initrute gets closer to it. Initrute is a mechanism where the first process of the Unix when it boots goes into a different route file system than you would have in the beginning. This actually is a workaround for not having Pivot Route because when you boot into a system with a RAM disk, for example, you then have Init already running and it's stuck there. So Initrute is a little just control or chem if it's in free BSD and that would then tell Initrute into this other file system which will be there at the time you're doing it and from then on all the other process of course on a PID1 will be in that new route. So you've actually pivoted into the other route that you've prepared and you can say in a way that's to pivot. But the problem is you're doing this at boot time and that's it because once you start Init, that's where you are. Now people sometimes say, well, it's also jails, it's also changing everything. The route becomes something new and you have a new network stack and some user in a jail doesn't realize that this is an upper route, super route above. So well, it's not that either. So the question is what is it? And I think the best way to understand what it is is to look at how you would look at the file system structure and see what you want to do. So obviously we have the root node where we hang off our file systems and we always have the root file system at least on small systems. We only have that. And that file system will then have somewhere down the line a directory which for these presentation I'll always call new root. It's a directory on the root file system. And on there you will then mount another file system. So to genose it covered the directory, the node, and this is actually a bit of the complication we'll see later. It covers that and you have the new root. Now the new root obviously should have very much the same infrastructure they have on the other root. So if you want to make that the new root, everything is where you expect it to be. Slash bin, slash sbin, var and everything else. Then in that file system you will have a directory called put old. Just a directory nothing mounted there. And now you want to pivot. So what you do when you pivot? You actually just simply move that and you're done, right? So not very hard, you would think. There's the only thing though, now root is in the middle. It's not on the top anymore so you have to relabel them. So now you have the end state where the new root has become the root of your running system. And the old root is on put old where you then can go and clean it up. I mean you usually want to get rid of the old root. That's why you're pivoting away from it. And you still have it under your new root to do things to it before you can unmount it. So we look at it once more quickly. And this is I think where the name comes from because it's quite an old name. And I'll show you on the command line how that looks like. So on an MPSD system, we usually have WD0, WD1 devices. And on a small system where you only have root and everything's in the root, this would look something like that. And here's a copy of root on the second disk. And I happen to have it mounted on new root. So we're talking about the system call, but obviously we have to have a user space program to call it. So this is a user space program which for all practice purposes, we can call pivot root. We can call it something else. I'll come to that later. And we say, well, my new root is on the mount point new root. And the old one will be on put old. I'm using these names because it's traditionally called like that in the system call I'll get to. When you do this, you get what you expect. It's flipped. WD1 is now root. It's on the mount list on top, because you want root to be first on the list in point. And WD0 is on put old, reachable from root.  So the question is, does it run? It didn't run for a long time, but it does now. So I was going to show you quickly. And I'll be showing another demo later on, more sophisticated. I just wanted to show you how it runs. OK, if I just screen. Oh, I see. I'm running a virtual box here with two disks, same size, two adapters so I can go to the outside. I can lock in from the inside. It's running an FBSD head. And here you can see it's running head. And it's a kernel loadable module. So I have to first load the module into subpoena. I can type when I talk.  I can type and then I just pick up the microphone and talk. So I loaded the pivot root module, because it's a kernel loadable module. So if I forget to do that, I just call pivot root. And it's not in libc yet, so then the user land program but since I loaded the module, then the code is there and the root can run. So actually, here I have WD0, which is root, and WD1, which is the root. I just call it like that. And I have a PROCFS mount, because that's pretty standard. So we're going to see something happen here. Yes, something else I actually do when I was debugging this. I would put a WD0 empty file there on WD0, obviously. On WD1, I would have one, because when you switch, you want to make sure, am I on the right one? They're not totally identical. OK, so we go. So put all of that directory. And now we have the situation. So you see PROCFS followed along. That's obvious. I mean, it was on the root before, and it went along. Just a mount now, PROCFS. I'll talk about un-mounting the other things. But my other demonstration will be running on a slightly different setup. So I'm going to now reboot the VM, and we'll look at it later. I'm going to hold it because I'm going to boot into a RAM disk later on. OK, so back to the presentation. So yes. So actually, maybe this is more an important question. I mean, first of all, it has to run. But the second thing is, why do we want this, actually? We've been able to live without it for 10 years. Piviru comes from Linux. This could be a reason why we don't have it in freebies, even because it's Linux. But also, there was no need for it really for a long time. So why have it? It just doesn't make sense. Seven years ago, I discovered that this was called Piviru, when I was doing my talk about RAM disks and so on. But there was the in-it-root solution that came out, and it was good enough to move into a different root when you were booting up. And so there was no pressure. But when you work with embedded systems where you want to upgrade firmware, or very often, your root file system comes in the way. So it would be handy to have it. And large boxes take a long time to boot. But you might want to fill up, upgrade the full user space, but you know the kernel won't update. So you just keep the kernel the same, but everything else can change. So what is actually Linux in-it-RD do? Probably most of you know who ever worked with Linux, but it's the two-stage boot process. They go into an initial RAM disk, and that's why it's called in-it-RD. And then that in-it-RD looks for the root file system. It may be loading more drivers and doing other things to get to the root file system. And once that's prepared, it mounts it, and then it pivits into it. This is, for most Linux users and even admins, totally transparent. They don't even know that this Piviru thing, Magic happens there. But then their system is finally booted. So at the end, you can free, of course, the initial memory, the in-it-RD memory, which we want to do as well. So on NetBSD or previous D, people now would like to also encrypt their root file system with CGD or Jelly or DVD on previous D. And there you need to do exactly this type of mechanism. You have to boot into something which has enough BSD infrastructure to decrypt the root file system, and then you can go mount it.  So right now, we, of course, do this with Piviru. Where does this actually idea come from? It's really old. I mean, that's how all the OS installs themselves. They come up, and they go into a Rambus. If you're here on, you have no writable media, and you take the memory to do that. And then from there, you prepare the media you want to install your OS on with manipulating the disk, and then later on using Truth to get it all down, and then you reboot into it. Now, in this case, you wouldn't have to reboot. You could just pivot root into it at the end, and the system would be running. Now, obviously, sooner or later, you will be rebooting, but you don't need to. And it might be good for a live file system, for example, where you're just never going to reboot because you don't need it at the end anyway. So this idea is not revolutionary. It's just an incremental change. The change, I think, is that you can do this any time, not only at boot time. And this is actually the firmware upgrade situation, where you're doing it at the end of the lifetime of an appliance where it gets the new image. It will actually do the pivot root to do it. So how would you do an upgrade of a firmware on an appliance? You would have a running system. You would take its full root files, which is maybe some read-only root files, as to which you would then put into memory on RAM disk. You would then pivot into that. So you're still running the same exact system. And this frees up the old root, which you then can do whatever the manipulation you need. And then you can do a second pivot root back into the new state. So obviously, this works perfectly unless you're upgrading your kernel, which then you have to first boot to take advantage of. There's also a situation where you might just have your hardest for some reason, software, or hardware not doing what you're expecting. You need a new root. And you don't want to boot your system because you might lose evidence for some reason. You want to keep your system running, have the old root there to maybe fix it, or investigate because once you boot, you lose all that information. So it'd be nice to go to a different root, which you know is safe, an NFS server, work from there, and investigate what's actually wrong before the system totally locks up. This is a picture from Windows Support. And they just say, well, you know, if you have a disk for a reboot, don't know how that's going to solve the problem. But there's a check disk, I think, on Windows 7, which is a bit more sophisticated. So they're selling that. It's a nice, cute photo here. So that's the description of pivot root. Hope it's clear that this difference between in-it-root, show-root, and pivot-root. And now the question is, how is this implemented? So I thought it was actually pretty easy to start, but I stopped working on it. I had a version in previously, which kind of worked, but it always panicked at the end. And I was never able to remove the old root. And every time I touched old root, the system would go haywire. And I lost interest because it just couldn't solve it. And I left it for a while from actually a few years. And then when I started looking at NEPISD code, I said, let's try again. And so you have to ask yourself a few questions. How do you want to do this? Earlier, I just put it into VFS mount and made it a different way and hacked it a bit to see could I get it to work. But then when I was doing it with NEPISD, I wanted to also become more familiar with kernel loadable modules. So I said, well, can I make a new system call? Because I want to make a system call which is actually Linux compatible. And I want to put it in a kernel module. So it's also a learning process for me. But later on, actually it occurred to me, and I also saw some code from Scott Long that had written something like that while it never really worked a few years back. And it was indicated into mount system call, which I think is quite attractive. Because you don't need a libc bump or anything. It's a different type of file system, you pivot. And it's all part of the mount system call. There's a drawback to that, too, that I'll get to that later. And you have a user land stuff. It's either the mount call or the mount program or the pivot root program. And that program, of course, could help you do other things before or after you invoke the system call. And the final question I had is, what do we do on FreeBSD jails? Because a FreeBSD jail, it cannot do the pivot root, per se, because it would turn the world inside out and everybody else would be underneath its own root. But you could imagine that the jail itself can do its own pivot root in its own little world. And actually, this is an attractive thing that I'll talk about later on. So what does the user land program look like? This is just the important part of it. It's really straightforward, totally straightforward. But one of the things that I think you have to do right away is to make sure that the input is validated. So you make sure that you can call relative paths or you call real paths so you get a nice string. This is important because these strings will eventually go into the mount list. And if you can do real path in user space, that's good. And obviously, that doesn't stop somebody from using your system call raw. But if they were asking for trouble, that's what they get. Here, if you use the user space, this will be guaranteed to have clean path names because real path takes out all redundant slashes and dots and anything else. And then you can do some more possibility checks here. This is visibly underneath each other and so on. And then you call the system call. So all the work, obviously, is done here. One more thing that you might want to do at the end is signal the process number one. And this is now I'm coming to the additional difficulty that you have when you do pivot route. It all seems so simple now, and I want to make it look this simple because it is quite simple. And the usage of it should be simple. But there is the problem that processes like one in it or other processes. They still have file descriptors on the old route. That doesn't really change. You'll see in the system coordination that we can do some things for the process, but we can't do everything. So maybe in the user land program, this is just about as we could do it this way, we could tell in it, OK, reopen your file descriptors on the new route. Kill hub. Yeah. Well, signal on C, kill minus one. Just something that a hub signal. Hub means paying up, reopen your file descriptors. So now about the system call itself, the other part, the hard part. When you're doing the pivot route, as to what you have to contend with the virtual file system for every structure. And this is actually the part which seems easy, but then it gets very complicated because it's all over the place and you can read the manual many times. For me, it seems straightforward, but it's not so easy. So first of all, the root V node, it's a global variable. So you're going to be changing that at some point. That global variable is in the kernel and you change it at the right point. You have V nodes to look at. I'll tell you what the V node is later, but they are connected to the directories that you're working with. There would be new route and the old put old. And of course, route mount point, the new route, and later on when you put the old route on the put old. So these are the mount points you're contending with. And all these structures have locks and references, meaning that when you use them, you raise a reference by one or if you drop the usage, you release the reference. And this has to match up otherwise, eventually there'll be a problem later on. You have the name of the local, which is the way that the path name gets translated into a V node. There's a cache there, so you have to make sure that cache is handled correctly. And then you have locks on the mount list itself, because it's one big mount list, and each mount point in that list again has locks. And actually, once you get all this straight, you still have the problem that the running process themselves, they all have a current working directory and route directory, and since you're shifting route, on them, they have to be told about this. So this is actually what we need to do. So how will we start the implementation? We first, I did it several times, but I thought this was the best way to do it. We first get the V node of the old route. That's where we're going to put the route later on. It's actually below in the route, but this has to exist, and if it doesn't exist, we don't even go on. Then we get to MVP, the new V node. This is all without locks. And we, oh, there is a lock. There's a lock in the beginning to go into the system code. There's a reason for it, I'll tell you later.  But then we get the route new V node, which is above the put old. And we check to see, does it all line up? Meaning that the put old has to be on the new route file system. New route has to be the route of a mounted file system. And it has to, well, if it's not under it, then we just exit in valid. Now, if the process that was calling this was actually not under new route, we should move there before, because otherwise we'll be blocking ourselves. So you silently move to the new route so that you're in the route later on when you've done the call. This is all preparation. And then once that has all kind of been set up without any locks, you go into the mount list and you start the first part. So you move it to the, then NMP is the mount point, and you move it to the top of the list so that when somebody does mount or DFA gets the list in the right order, it's also necessary because other places expect route to be first. And you then, for each of the mount points, adjust the stat FS, which you saw happen with PROC. And then this is where you do the main work. And you don't adjust route keynote right away because you have an interesting routine in NEPIS, you know, so in pre-BST to signal all the processes that the route file system, that their mount point has changed. This is used by the mount system call to tell a process, listen, you may be underneath a file system which now is being mounted over you, so they have to move over on top. And the interesting thing is that routine actually changes the route keynote when you call it from the top, from the route keynote itself at the end. Now, there's different cases in pre-BST slash DEFFS, the device DEFFS, so you actually would have that moved down too, and that would be very bad because anything that would happen right after the pivot route would not find slash DEFFS and would probably fail. So you actually do it the favor and inside the system point also move DEFFS back again to the new route node, you hook it up there. And in pre-BST, in NEPIS you don't need to do this because there's no DEFFS, but in NEPIS you have to update the current route variables. And this is not so easy because they're actually, well, it's obviously doable, but they're constants, they're read only, so probably have to delete them and remake them for the new route. That part I haven't done yet because I discovered it just recently that I want those to be correct. If somebody looks at this control variables, they better be correct. This could be misleading otherwise. So, I'm not gonna go into the code further because once I commit it and I will, we can then all look at it in detail. But what do you have to work with when you're doing DEFFS? You use the name I, and it's infamous name I. Everybody wants to make it better and do its thing, but it's complicated. But the nice thing is in NEPISD we have the name I simple kernel and the name I simple user, which are actually, they do most of the work that name I does for the most common cases. So kernel means that you take paths that are from kernel space and translate into V nodes. And if you have user space data, when the system call gets started, of course, it's user space data. You get a V node and you copy the user space data into kernel space and then get the V node of new route and put all. So once you have that, either through the end in it name I or through that interface, you then can reference them with, you change references by calling Vref or you release reference by counting down with V relay. So these are inverse operations. You can lock a V node. If you need to change these, you have to lock it. You can decide to lock it immediately when you're doing the name I call. So it's inside the name I call with a flag you lock it. Or you can lock it explicitly with V in lock. And obviously when you're filling with the V node you have to lock it. But I saw that it was much better to actually lock towards after all the validation because you don't want to accept, you don't want to lock against yourself. If somebody gives you two V nodes on the same file system, you could lock against your own route file system, which then of course makes the kernel hang. So you do it unlock when you do the validation to make sure that the V nodes are underneath each other and then you get the locks to do the work. And then the mountless locks are our mutexes. This is for the whole list once and this is for each mount point. And this actually is the magic sauce. When you call this after you've set up all the right things, you call mount check tiers with root V node. And it realizes that you've set this up that root V node is actually mounted on new route. And at the end of telling all the processes that this is your current working directory and the root directory has changed, it will then actually change root V node to new route for you. And this part actually is in VFS mount C already and it has the correct locks. Works very nice because in a way you are mounting root once more somewhere else. So when you're doing this, how do you debug all this? I had to, I just couldn't write down a code to make it work obviously. So I had to do debugging and on NEPST, I found some nice routines that do this for you. So you get output like this and you stare at it and stare at it and try to find out, well, the reference is there. Important, they go up, they go down and if you do it the wrong way, you then have either hang or panic later on, maybe much later on, that's the interesting thing. Then you need to look at the flags because you will be changing these. And if you're mounted, you're holding the V node as well. And you may have locks so you see that there's a lock and this is a V node which then you can match up with another V node you're watching. So by looking at this debug, as you're doing the debug you see what's happening and you see what you need to do to make it really work. And mount points very similar because obviously they interact with each other. Mount points cover V nodes and then you see which V node is being covered by the other. You can see the flag if it's through files or something else or where is it mounted. And then also the status information which is down below you see what the paths are which of course you're changing as you go through the mount list. So these two routines are actually already there you just have to print them out in the right place and you obviously have to look at the different V nodes and they're obviously pointing to each other because it's all forward and back linked lists. So you can find out where the V node is actually mounted because the V node may be down somewhere but it's mounted up on a root or a newer. You know you have a mount point, yes. And then if it's the mount points there you may be covered so you have to look at this which we of course modify. And then in certain cases you then have to follow right through and I look at all these to get the references and locks correct. So what things can go wrong? Well I thought I was good at security and I had called which kind of was working for a long time and I didn't think about well if I let somebody do pivot route out of a true environment this is gonna let him see everything and everybody else will see nothing so that's actually very bad. So the first thing that this system called us is checks to see am I being called from a true environment and if so be prone. But this was not in there for a long time so it would have been a gaping hole. When you were validating input I first was using locks and then in certain situations where I would give it mount points and new route was on the same path so it would lock except the system would freeze. And then I would have to go into the debugger and find out why is it doing that? Oh it makes sense and so on. And there's also a problem with when you're taking two v-nose and you shouldn't lock two v-nose at the same time because this opens you to a potential race because you're trying to get one and another it's just a course of somebody else is getting the other one in your deadlock. So I haven't found a way to get this solved completely but since I'm breaking the rules in pivot route I make a lock around pivot route and I also have the lock on the mount list and I try to keep that area very short but I need to have both the put old and the old route locked at the same time to the flip the certain values. Can't do it one after the other. I may be able to fix that still but to me it seems I need to have both locks at the same time because I'm getting a value which I then put in the other one. And okay phenomenally what happens, when you look at your system after you've run a buggy pivot route a few things can happen. For example, it may seem to have worked but you panic when you reboot which means that some reference has not been counted down correctly and it will tell you when it reboots. Oh I couldn't even unmount this thing it's too many references and then it panics while it's shutting down. So that means there's a problem in your code and it's latent there but it doesn't really seem like there's a problem until you do the reboot. Or obviously if you access the old route under the put old it may lock up your system.  So this actually then shows you how complicated VLS can get if you try to fiddle with it. I talked about the processes which run on they get moved, their current working director but their file descriptors of course the processes have to contend that with themselves. I mean there's no way for a secure shell demon to know what to open on the other side so you have to restart these services. So that's something that actually the administrator is left to do. He only has the possibility to work with put old and make sure that all the processes remove their references on the old file system. So that's how much I've done till now and this is what I tend to do. I'm still not so sure if all the code it's not really much code actually it's one or two subroutines that I could actually put into the mount BFS mount code and just support the minus T pivot option. But the drawback is that then it would always be in the kernel and making assist control turning it on and off. I mean it's just added aggravation for root to have to do because either you lock it out and then you have to reboot anyway which is kind of pointless because you were trying to avoid to do that. And if you make a kernel module then of course it's isolated. It doesn't, your kernel proper doesn't have this but anybody can load it, right? So I have this right now which means it's easy to do this. It's probably harder to do this than this. It's better to isolate from the rest of the tree. Then once that's done obviously it's nice to support the pivot root system call. So you would have it in the system calls that we have the compatibility library which is have a little shim to also support that. And I still need to update these persistent variables. And when I have running code for NetBSD but I do plan to do it also for a previous year with some help from previous people and then this last step has to be done. But this is actually the easy part because it's just moving one thing straight forward. And in NetBSD you could ask well should the kernel also look at is there a Dev PTS and if so move it over because there may be network connections and so on but I'm not so sure I'm gonna have to discuss this on the mailing list if we want to do this. I think it's the Unix philosophy is to do as little as necessary and let other programs put them together, these small imperatives put them together to make the complete solution. So I'm gonna show you another demo a bit longer. Yeah, turn off time. And what is it? It's actually a very large NetBSD kernel, 23 megabytes. I didn't compress it, I didn't strip it, nothing. And it's a generic install kernel, current head. And it has an eight megabyte RAM disk built in. It's a custom RAM disk with a secure Sheldemon running and a root file and a root home directory and SSH or thrice key so I can log in to the system from the outside, I'll show you this. So I'm in the RAM disk. And then I will, well PivotRoot came out as already loaded or I could load it and then we'll switch like I did before. And then I will restart all the processes to in fact get rid of all those file descriptors still hanging off my RAM disk this time because this is my root. And then I unmount the RAM disk and you will see that my system is still running and I can still log in from the outside. So this is a way, this demo is showing you step by step the init RD of Linux, I mean 10 years later. But it's instructive, it also shows you that it works. And I'll just now drive this through and then later on we can maybe do some questions. We probably do one question now. Reason being that when I boot this thing into the RAM disk, you will see it will take a very long time. Well a very long time. I think it takes about 30 seconds. Because NEPISD when it boots and it discovers that slash def is empty. Def console is missing. It says, oh I have to make the def file system. So it makes the def, it uses shell make def it makes all the devices. And that takes a long time. Because I mean this is a virtual machine so it's not fast. And eventually we'll see it making these devices that unfortunately takes some time. So you can't really see it but it's all green until it discovers the root file on NEPISD and then it's doing this thing which takes 30 seconds. And then once it's done I can then log in. Also in virtual box when you start with a different DHCP setup it changes the IP. So it's not 101 anymore. It's 14. And I'm gonna have to log in as root here because I only have root on my RAM disk. And I have a secure shell secret key so I don't need to type a password. So did something happen back here? Yes, it's finished. It started secure shell demon, blah blah blah. It actually loads the pivot root came out because if I forget I get caught on this kind of ugly in a demo. So it should be listening here and I can then log in. Yeah, first time. Oh, yeah, well actually I shouldn't do this.  It actually just doesn't store the secure shell keys because if it's a one time thing you don't want them stored in your known host because it'll just pollute it. So ish, it doesn't do that.  So I'm in the RAM disk here. Root is quite full, 99%. But I have a nice selection of programs here. It's crunched of course. So, whoops. Yeah, typing with one hand and then turn caps a bit bad then. All right, so there's 116 commands that are called crunch together here. It's very similar to rescue but a few have thrown out and a few have put in to have the networking part in. And it's all crunched into one program obviously. And pivot root's crunched in there too. So now we have the MFS, this eight megabyte thing and we have, well there's nothing here yet because it's all in memory and now we mount the device W0A, the one that we looked at before. Yeah, slash is missing. So we, excuse me. Yep, good hint. This is far up as it can go. All right, so yeah, thanks. Hold up plus. Sorry, so this is the root we had before. Two handed works better and we pivot into it. Okay, so that's what we expected. But actually I should have showed you the PS, the which processes were running because we have to now take care of these. So yeah, what do I do now? I actually, this is the last command I'm gonna type in. I'm gonna start ETCRC. This is what actually it would do. And the funny thing is actually it's reconfiguring this network again obviously because it was the system with the config in RCConf. And I get to keep, this is a network connection obviously. So I get to keep that network connection and now I could actually go in. This of course will fail because I can't check root. It's a rewrite but that doesn't bother me. But now I have all the stuff already running in the new root. And I could now unmount the old stuff. But I'm not gonna show this. So we're done and maybe it's actually time for one smart question, maybe. And then we can go to lunch. So, right. I'm intrigued about the possibilities of the Linux simulation of doing the Cisco. Can you think of a use case? Well, intrigued. The thing is we have a list of Cisco's that we can emulate with changing a few arguments or something to make them work the same way. Now, I don't think there's a large use case because Linux, why would you be running PivotRoot on a Linux application? This is operating system infrastructure. But since we have Pivot, we would have PivotRoot. We also put it into the compatible library just because we have it. Not because we expect an application to actually use it. And if it would use it, it would not fail. It would work. So I agree with you, there's really no real use case because if you're doing PivotRoot, you're probably running Linux anyway. I had a question myself. And I don't have an answer to it, but we have a very cool testing environment on NetBSD. And I don't know how we're gonna test this though because when you test PivotRoot, you're changing everything and the whole test automation will then, of course, start failing because it's in the wrong route. So I don't really know how we would test that. With the freebies, GGL, it would be easier because you put it in a GGL and it would do it in that level and you could look at it from the outside. So, what? Yeah, but still you're changing the route. But how would your Anita's test suite then continue in the new route? Okay, so the basic idea is you can use one to one user land kernel and you could mount some parts to it. You can also bind additional processes to a degree and then you can do the PivotRoot and test that. Okay, so the missing link is having the kernel running on the rump. Yes, okay, yes, that's actually a very good point. So I have a way to test it, I have no excuse. Okay, I think otherwise, feel free to come and ask me questions some other time. And have a good lunch.


conference happening in Chicago today. From elections to COVID to the war in Ukraine, misinformation is everywhere. It's like information pollution at this point. It's part of the atmosphere, choking all of us. Well, yeah, I mean, I suppose that's right. I think anything from that would be deviating from the norm. Now misinformation may or may not even be deliberate. I can spread without trying to mislead anyone. If I give you the wrong directions to an address, that's misinformation. Purpose, I'm not trying to hurt you. I guarantee it. Would I lie? Hey, welcome back, everybody. So according to a new report from the president of New Cycle Media, John Nickacea, a recent informal discovery board meeting with the largest shareholder, his name is John Malone, referred to CNN's Brian Stelter as, quote, a cancer and a zucker henchman that has a failing show.  The guy is one of the most discredited people in media. This much deserved news comes as Helter Stelter desperately tries and put a positive spin on his part in covering up a legitimate story about Joe Biden's corrupt son just before an election by masquerading as an authority on disinformation. I guess you could say that as a guy who helped spread Russian disinformation throughout the country in the form of the steel dossier and actually claimed that Trump was a Russian installed by Putin, that Brian Stelter is in a sense an authority on disinformation, just a distributor of it. We're gonna get right into this clip, but right after I tell you about this free offer from Noble Gold. For years now, people have been setting up a little contest between crypto and gold. Both carry stuff and they travel from A to B, but they do different jobs. Gold's job is to keep the value of your money safe and preserve its value. And since Ukraine and the oil and inflation crisis, it's done a brilliant job compared to stocks and other investments. So if you're worried about what's going on right now and who isn't, just talk to an expert at Noble Gold about precious metals IRAs for your retirement. They'll put you straight on your options and hold your hand through the whole setup process. And this month, for any qualified IRA, you'll get an incredible three ounce silver American virtue coin completely free as a thank you. Call<PHONE_NUMBER> now to find out more or visit noblegoldenvestments.com. That's noblegoldenvestments.com. You can also check out the link in the description or pinned comment. Make sure to tell them drone tech sent you. That one almost sent me over the edge. This is the same guy that said this secret email. The post claimed that the emails were found on a laptop computer that was brought to a repair shop in Delaware in the spring of 2019. For all we know, these emails are made up Russian disinformation. This is a classic example of the right wing media machine. The same guy that said that, the very same guy just said this. Now misinformation may or may not even be deliberate. I can spread without trying to mislead anyone. If I give you wrong directions to an address, that's misinformation. It's not on purpose. I'm not trying to hurt you. Oh, come on.  The guy is trying to manipulate his part. One of many that tried to manipulate an election in favor of Joe Biden down to this. It's not on purpose. I'm not trying to hurt you. It's harmless. Don't blame him. He's not trying to hurt you. It's intended to circumvent the truth. We're seeing this from bad faith actors, including autocracies around the world.  God. No, it's okay. It's okay. The guy is going to lose his job soon and end up flipping dildos at walls for stories on Vice News, given how completely utterly lacking and self-awareness this has all been and just filled with lies. I'm dying to see what this so-called disinformation conference is all about, which I'm now expecting to literally be a conference that pushes disinformation. Let's take a sort of amazing. Thank you. Thank you for doing this. Really appreciate it. I'm Daniel Schmidt. I'm a freshman at the University of Chicago. My question is for Miss Applebaum. So in 2020, you wrote, those who live outside the Fox News bubble do not, of course, need to learn any of the stuff about Hunter Biden, referring to his laptop, of course. A poll later after that found that if voters knew about the content of the laptop, 16% of Joe Biden voters would have acted differently. Now, of course, we know a few weeks ago the New York Times confirmed that the content is real. Do you think the media acted inappropriately when they instantly dismissed Hunter Biden's laptop as rushing disinformation? And what can we learn from that and ensuring that what we label as disinformation is truly disinformation and not reality? My problem with Hunter Biden's laptop is, I think, totally irrelevant.  So I don't find it to be interesting. I mean, that would be my problem with that as a major news story.  A Democrat state media propagandist doesn't care if it was disinformation or not, even though that's exactly what they claimed as justification for censorship of the story. But none of that's a problem anyway because she just isn't interested in the story. She, a Democrat, thinks it's irrelevant. Irrelevant to who the president is. Despite the president being the father of the guy who has shady business dealings with China, Russia, and Ukraine. There you have it, folks. They think that censorship is a completely legitimate tool, as long as they, the gatekeepers, the Democrat Party, deem the story irrelevant. Well, that's about enough soul-damaging gaslighting that I can take for the day. If you did enjoy it or found it informative, please hit that like button, share, and let us all know what you think in the comments. Thank you.


In episode 3.3 I'm going to be showing you guys how to create an automatic upgrade unlock system which requires you to reach a certain point in order to unlock certain upgrades so such as this I'm going to reach 25 flash and we're going to unlock the next upgrade just like that. Hello everyone it is Crypto Grounds here welcome back to another Unity out of games tour video this episode 3.2 and today we're going to be doing the unlock system for our upgrades so how this is going to work is that upgrades are just going up here on the screen when you reach a certain point so you can kind of have the unlocking the unfolding mechanics I guess you could say and this is very easy to do it's just a few lines of code that we're going to be doing if you end up enjoying this video make sure you leave a like to support the video if you aren't subscribed already make sure you hit that subscribe button and turn on the notifications for future notifications of videos and live streams so anyways what we're going to be doing is heading to our upgrades manager script so in our upgrades manager you can see that we have created click upgrades and production upgrades so these belong to the object upgrades so we don't have let's go to our upgrades real quick so you can see that we have our upgrade button level name and cost so our upgrade button we can assign an image to this and turn it off or we can actually just turn off the game object of this object so we're going to do that instead and that would just be reducing the amount of adding we have to do what we're going to do is create two big double arrays so this would be click upgrades unlock we'll do the same thing for production and in our start method we will assign whatever number we want our things to unlock at so let's say we want the first upgrade to unlock immediately we can just have that at a zero so we're going to create new big double array and we're just going to set the first one to zero because we want to see that right away and our first upgrade it's cost is going to be 50 let's make that 25 and then the next one will be 50 and then the next will be 500 so I'm basically doing half of what the original cost is you can simply do 990 or 40 if you'd like so you can do whatever you want with these but I'm just going to do half of the original base costs and I'm going to do the same thing for our productions upgrade deluxe and except I'm going to be adjusting these two whatever I want all right so here our two additions inside of the update method we're going to be looping through all of the upgrades inside of our list so to do that we're going to be making a for loop so we're going to start at zero and we're going to go to the click upgrades count and we're going to increment i by one and now to access this we need to do click upgrades at index i and we're going to turn it off on and off by doing that game object set active and for our set active we're going to pass in through controller dot instance dot data dot flask is greater than equal then click upgrade unlock at index i so now basically whenever our flask is greater than the cost the base cost it's going to turn it on so the current issue with this is that it's going to keep turning on and off whenever our flask is not or is greater than this unlock so we want to make sure we want to keep it on when it's on so what we do here is if our click upgrades at index i dot game object dot active self so when it's not active already we want to check to see if we can make it active and we'll do the exact same thing for our production upgrades awesome sorry if this is really slow I don't know why writers lagging like crazy now obviously if you had more upgrade types to do you could you can make a method for this and just keep these separated but we only are doing it twice so I think we're fine so one thing we notice is that these are all on so when we create our upgrades right here they are turned on by default and that means this will be ignored entirely so we actually have to turn these off before we do this so let us do that so in our start upgrade manager we're going to head to our for loops here we're going to add a line in here we want to turn off this game object after we instantiate it so we're going to do upgrade dot game object dot set active false and we're going to do the same thing for the other for loop and we should be set so now you only see one upgrade which would be the click and the one flash per second so let's get some flash going and there we go we had the second upgrade and we could do the same thing there we go we got another one and I think the next one unlocks a little further down the road but you get the point anyways guys I know that was really simple and I hope this is useful for some of you guys who are interested in this if you did enjoy this video and you found it helpful make sure you leave a like share with your friends subscribe to my channel if you aren't already and turn on the bell for future notifications of videos and live streams thank you guys so much for watching all the way through I will see you guys in the next one in episode 3.3 and this will be on generating upgrades so this is kind of like anti-matter dimensions if you haven't heard of that game you basically have units or upgrades producing each other so this will be kind of an interesting topic to discuss and it will require some expansion have a great day night everyone and I will see you guys in the next one thanks for watching again peace


Firstly, we have implemented a whole of school pedagogical framework, the Art and Science of Teaching, of which as you would know, deep learning is a significant part of the ASOP pedagogical framework. So all teachers and students are exposed to that and teachers use that as a medium for consistency of pedagogy. And it's important that teachers have a common language around teacher talk and around learning and teaching and pedagogy in our school. We also provide time for teachers to plan and assess together, which provides that opportunity for teachers to share ideas around deep learning and around how they may develop deep learning in their students and deep thinking. We've reduced the number of assessment items over the last two years, particularly in the junior secondary years, to allow for time for deep thinking and deep learning. So we've moved from a situation where we had in excess of 15, 16 assessment items per semester for years 7, 8 and 9 students, and we have reduced that number to about 11 across the semester in each of the years, 7, 8 and 9, to ensure that teachers have the time in the classroom to develop deep learning and develop deep thinking skills and opportunities for their students and to provide that opportunity for the analytical and the critical thinking and learning. I've also employed a thinking coach in our school who works directly with teachers to develop deep learning opportunities for students. We have encouraged teachers to set open-ended assessment tasks as well, which provides that opportunity for students to collaborate and for students to engage in learning opportunities that are not closed, that are open, and that really provide the opportunity for students to guide their own learning and guide their own thinking and engage in some discovery and experiential type learning. Problem solving is a big part of that process. We also provide opportunities for teachers and students to move outside of our school and develop relationships with higher education institutions so that we can broaden students' perspectives and horizons. So for example, we work very closely with, of course, the University of Queensland, but we also work closely with the problem solvers of Australia. We've made some significant links with Melbourne University High School, for example, which is one of the other leading schools in the teaching of philosophy. So our staff work very closely with the staff, the teachers of Melbourne University High School, and together we're sharing assessment items and sharing learning opportunities. We've introduced the teaching of philosophy in years seven and in year eight at our school as well, because we have an elective subject of philosophy, critical thinking and critical reasoning in years nine, ten, eleven and twelve. So it makes sense that we expose all of our students across years seven and eight to the subject of philosophy and teach students how to think from the moment they come into our school in year seven. I think one of the greatest challenges that I have is the issue of consistency because of the size of our school, the number of teachers, the number of students. So it's really important that we provide a number of different opportunities at a number of different levels for teachers to engage with deep learning and for teachers to be reminded of and exposed to the concept of deep learning. It does take time, it takes experience and it takes a lot of time for teachers to work together to prepare and plan. So it's not something that our beginning teachers necessarily have from the moment they walk into our classrooms, but it's something that we nurture in our teachers as they begin at our school. Coaching and modelling is really important and we encourage team teaching as much as possible in our school. In fact we've allocated some of our investing for success funds to break down some of the walls between classrooms to enable some more team teaching across classrooms. We also very much encourage teachers to spend time in each other's classrooms and critique each other's teachings. So we've been doing that for about two years and we've seen some really tremendous results in relation to the feedback that our teachers are providing to each other. We've also found that it's very very supportive and significant for our beginning teachers to engage in that process. Challenges that we face in a large secondary school in relation to the concept or teaching of deep learning is time and consistency. Time and consistency would be the two biggest challenges. Our teachers are very committed and teachers generally right across the board I believe want to do a great job and are very committed to their profession. But it takes time and it also takes time to develop the confidence to engage in collaborative learning opportunities, to engage and take the time with students to provide the time for the students and have the confidence to let go of some of the content and really provide that time in minutes, a significant number of minutes in each lesson to give our students the opportunity to develop their deep learning understandings. Recognising that students and teachers all work at different rates. They learn at different rates. They work at different rates and they understand at different rates. One of the other significant challenges I guess is providing time in the curriculum to teach thinking. We have a very overcrowded curriculum in Australian schools and that's a real challenge to be critical around the content that we deliver and to address the overcrowding and provide the opportunity for deep learning and deep thinking. The other thing I need to keep in mind is the opportunity to celebrate the deep learning in our school and that's really important. We take the time to share our students portfolios, take the time to share our students learning with our community so that we demonstrate the significance and the value of deep learning in our school.


We'll guarantee Russian Army won't respond on this, Kate. Understood, Alex. Just locate the gas. Comment here, Markov's trucks, and get off the X before the tide turns. Copy, watch your full handling. All hitman teams, push to your 7th point. We need to keep this on a tight loop. Markov's men are moving the gas tonight. What are the odds we'd run into General Markov? Russian General wouldn't be caught dead out here. Tosses! Waits-y! Makashis! There, Kate. Drop him. We're clear. Copy. Post up on the ridge and let's get the lay of the land. Echo 3-1 to Blue Viking 5. Call for fire. Stand by for target confirmation. Copy, 3-1. Viking standing by. Let's wreck the area and make sure there's no Russian Army down there. Medium transport truck. Transport for the gas. Vehicles on the move. One in the tower, left side high, not Army. Movement in the rail yard. That's how the gas comes in. Patrol vehicle, contractors. Security, good targets. No Russian military presence. Call it N-C-I-A. Blue Viking 5, this is Echo 3-1. Troops in the open, south gate. You are cleared hot. Roger, 3-1. Target acquired. Weapons free in five seconds. Five seconds. Edge Viking, we'll take it from here. It's clear. Force up. Looks like someone was playing with fire.  Put them out of their misery. Element of surprise is not on our side, boys. Stay frosty.


Hey everybody, this is Brian. Welcome to the fifth Flutter tutorial. All right, so we're going to jump back into Dart here where we left off. And if you remember, we left off on flow control. And a lot of tutorials start things backwards, in my opinion, where they will talk about a variable, then instantly jump into functions and classes, which make no sense at the time because you don't know what you're doing. So what we've done is we've started with what is a variable, what is a const final, and then we've gone into flow control. How do you actually control the flow or the execution of your program? And we did the F, L's, for loop, while break, et cetera, et cetera. What we're going to cover now is going to be functions. So what are functions? But believe it or not, you've actually worked with them already. This main right here is a function. Main is actually the quote unquote main function or the entry point of your program. What I have here is just a new Dart program. And it's straight out of the template here as soon as you open it up. And you see how it's got this in calculate here? That's also a function. So really all a function does is break up or chop up the scope of a code. So we can do things like, let's get rid of this because we haven't talked about that stuff yet. Let's say print hello. Just something simple like that. We just want to print the word hello. Well, print is also a function. Somebody has already written that code for you and that's how you're able to call it. So let's say we want to print nine times. So we're going to say four int i equals zero i less than nine. Actually i less than eight. And i plus plus. Let's do this. One, two, three, four, five, six, seven, eight. That count that wrong? One, two, three, four, five, six, seven, eight. I did. Silly me. I had it right the first time. My brain's fried. Sorry about that. It looked funny. That's why I was like, what is going on here? All right. So we just printed nine times. We've done this before and this is old hat. But what we're going to do now is we're going to take this guy and put it into its own function. Actually, let's not call it Tesla. Say hello. So what we have here is we've created our own function called say hello. And it has this weird thing called void in the front. A function is always precluded or preceded by a return type. So we can return a variable from this function if you want. Void means that we're returning nothing. It's just void of any return value. So if you see the word void and you're wondering what the heck to call that, you just simply say the name does the exact same thing. What we've done now is we've broken up the scope of this a little bit better. If we flip over here, one of the things is called lexical scope. Dart is lexically scope language, which means that the scope of the variables is determined statically simply by the layout of the code. And we've talked about this before, right? So here's your top level, which is visible by everything. That's visible by everything. But the inside function is not visible by that. That's called lexical scope. We've already learned that. So what we're doing now with functions is actually breaking this up even further by saying, for example, this I is not available here. We could even say int age equals 99. And we cannot here say print age. It doesn't know where age is because of that scope. Age belongs to this lexical scope or this block of code.  So what a function is is basically taking scope, chopping it up even further and saying, I want this function to exist. And this function will only know about things that are inside of this function or this lexical scope right here. So, for example, if I say int age, well, let's not say that in cats equal and there's nine cats. I don't know why I'm fixated on the number nine today. And here we try to say print cats. Well, guess what? It's not going to work. Undefined name cats because it's not in the same scope. So let's get rid of those and get rid of that. So that is a very, very bare-bone basic example of, you guessed it, a function. Now, what we're going to do here is we'll say, avoid annoy. We all have that one annoying person, right? In our lives that just loves, loves, loves to talk. And we'll say int times. And here we're going to just say for int i equal zero i less than times i plus plus. I think my music stopped playing. Did my music stop playing? Nope. It's just very quiet for some reason. I have to play music in the background or this wireless headset will actually shut down on me. It's kind of weird. So some videos you'll actually see me flip back and forth. But so we've got our four statement here. Very similar except for instead of, you know, just hard coding it, we're doing this. So we're going to say blah. So somebody says hello. Let's actually leave that the way it is. But then the annoying person, annoy. And let's say they come up and they just start talking. Blah, blah, blah, blah, blah. You know, we've got a thousand blahs in there. I mean, at this point, you just want to like walk away and tell them to go bye-bye or something. So what we've done here is we've actually passed a variable to this. Believe it or not, this is a variable. Someone says it's a value. But remember everything in Dart is an object. Therefore that is actually a variable under the hood. This is called a parameter right here. The cool thing about parameters is what we're doing here is we're actually creating another variable and that variable exists within the scope of this function. You can see how we're accessing it right here. And we can actually say things like, let's get a little funky here. Eye of my phone made a weird noise that I never heard before. So here we're going to say, you know, what number of the number of times we're supposed to do it and then they're still talking. So let's just run this again and you can see. So we obviously have access to this variable. And there's really no limit to how many times you can actually do this. So what we can do here is we can say, avoid really annoy in times. And then we could just say, let's grab this. Now this may look a little weird and I wanted to do this as an example. So let's grab this and let's just comment these out here. So what's going to happen when we execute this code? Well, really annoy is going to get called, which is this guy here. What really annoy is going to do is it's going to actually call annoy, which is going to do this. So it's five by five. You can see how it does this and it just goes over and over and over again. So that is a function calling, a function calling, a function kind of thing. You have to be kind of careful when you're writing functions because you have, and some people are going to argue this isn't really the case, but you have a bit of encapsulation. And what this does is this gets a little confusing if you're not used to scope. For example, in really annoy, we have int times and times is here and times is here. However, this times right here, while it's the same value is probably not the same variable unless starts doing something under the hood where it, you know, because it's the same value that moves it in memory and blah, blah, blah. It's not the exact same thing. It's got a completely different scope and therefore has a different access. So we can actually change this name to blahs. And notice how now we've got to change it here and here, but it does the same thing. So looking at the functions inside of the tour of Dart, you can see there's other things like bool. And of course, now that I'm looking for it and there's void string, things like that. So you can actually make some really cool functions. So what we're doing here is just recatenating two strings together, actually technically three, because this is also a string and returning a brand new string object. So let's go ahead and do this. Okay, so we have my first name and my last name. And you can see how it just prints out the string Brian Caron's because what we're doing here is we're calling this function, which is taking two parameters now and returning them as one variable. Now, if you're wondering what types of variables you can return, well, any type of variable, any object, actually, you could actually even say there for unknown type. Ooh, actually, no, you can't. The return type can't be there. I stand corrected, ladies and gentlemen. I thought you could do that, but you cannot, which makes me wonder if you can return an object. You can return an object. Okay, so if you have no idea what you're returning, it's just some thing you can return an object. But in general rule of thumb is you don't want to do that because then they have to either know the type or they have to use something called reflection to go in and figure out what type of variable it actually is. Dart is a static type language, meaning that you have to declare your variable types. So let's go back to the tour here. There's some really good examples. And if you're not familiar with any of this at all, you should really kind of just play with some of these and figure out what they do. One thing you can do is default parameter values. And this is really kind of built into almost just about every language here. And what you can say is, you know, your function can use an equal sign to define the default values for both the name and the positional parameters. The default values must be compile time constants. Must be compile time constants. If no default value is provided, the default value is null. Meaning, because these do not have a default value, they are null. Null means there's just nothing there. It's like nothing else. It's like when you lose a puppy, you're just empty and void inside. So what we can do here is where to go is we can turn one of these into a default value, right? So let's just say this. Not good at call of duty. And it says positional parameters must be enclosed in square brackets here. So what did we screw up here? That's a good question. What did we screw up here? This thinks it's optional here. Let's try that. Brian Carons. Now, if we admit this guy here, we suddenly get Brian not good at call of duty. So what we're doing here is we are saying, this will be null. We are not assigning a value to it. It must be assigned and there it is here. So if we just delete this, you notice how suddenly it's going to freak out and say one argument is required but zero found because this is the required argument. We haven't supplied anything and you can't actually do anything with it. So you could actually pass null which may actually make this thing explode. Let's find out. See the method blah blah blah was called on null. So we're saying null plus these strings. So Brian, let's just change this to is not because I'm really not good at call of duty. I've been trying to get better at it but I'm not good at it. Let's run this again. So we've supplied the first value and it's not null. And then we have this optional and we are assigning the value to it. So because it's optional and you have to include any optionals in these brackets, you don't need it. You see how there's no name here but the minute we include it and let's say let's just do my name again just because why not run this then suddenly it is not using the optional parameter because we have supplied the value. Once this value is in here gets converted to a variable and it replaces this. So think of this right here as like a temporary variable and let's say let's just call this string extra. Let's make that lower case just because I'm kind of snobby about that these days. And we're going to add the extra in there. So you can see how we actually can do this, right? So we have Brian which we've supplied Karen's which we've supplied. We did not supply the extra. Therefore it's going to use the default and we could actually take this out right here and you'll get two of them. See how that works. So as you can see, functions are pretty neat. There's a lot of things you can do with them. One thing you should really be aware of when you're creating your functions though is the actual scope of the variable because sometimes things can get a little squirrely and you're looking at one function and because you've named the variable the same in many functions you think that variable goes everywhere and it really doesn't. So that's all for this tutorial. I'm going to keep going down. There's actually a lot to cover on functions. So we're going to make a couple of these. But if you found this helpful or even moderately entertaining go ahead and visit voidrums.com for the source code for this and all other tutorials. And if you're so inclined go out and join the voidrums Facebook group. There are 1720 members out there right now all walks of life all programming languages. So definitely check that out. Join up become a member and help everybody else out. Thanks for watching.


Respect, it's one of the greatest gifts offered to anyone, absent of time, space, or position. In leadership, your position becomes your lesson. When applied towards others, showing respect is not an agreement with the position of others as much as an inward reflection of your own beliefs, assumptions, and behaviors. Respect is hearing, sensing, and allowing permission for the other person to maintain their beliefs and perspectives as you analyze your own. To perfect respect, a leader must humbly reinspect our held assumptions and inherent biases for potential correction. It was once said that the only way a leader can be right 100% of the time is to change your mind when you are wrong by Yisrael Bruehmer. Pause in your self-learning and decision-making. Offer others the greatest personal no-cost gift, a reinspection of your own positions while allowing others to have their own. Lead well with respect this week.


This study investigated the reaction mechanism of COXR, oxidation of CO or CO2 to produce methanol. A single co-atom was used as a catalyst to observe the reaction. The results showed that the reaction rate of COXR was much slower than CO2R due to the lower energy barriers involved in the formation of a hydrogen-coordinated complex. This complex was found to be responsible for the slow reaction rate of COXR. This article was authored by Cinewen, Jianjiao, Zunning Li, and others.


Hey, everybody, it's Craig Matthew here, and in this video, I'm going to show you how to improve your photos, regardless of the camera you're using, just by thinking of complementary colors. Now make sure you stick around to the end of the video, and I'll walk you through step-by-step how we created this shot. All right, let's get started. Anyway, here we are looking at the color wheel. So one thing that you might want to consider is working with complementary colors when you're taking your images. So for example, skin tones are made up of reds and oranges and yellows. So sometimes by adding blues to the background, you can really cover the whole color spectrum. So you could see here, you've got orange and you've got red, and then you see you've got your blue and your violets. So I was looking at this color wheel and I came up with an idea. So we did a behind-the-scenes shoot of it, and I'll show you the image right at a camera here. I'm just going to close this. Now we went to a cave. So I live in Halifax, and there's an old British fort downtown, but at the mouth of the harbor, there's an outpost where they used to store ammo. So there was some caves. So we went to these caves and we looked around and I thought, wouldn't it be cool if we did like a blue gel on a speedlight and made like an ice-like cave? And then you've got this Amazon kind of greenery outside. And I kind of had this concept. I didn't know if it was going to work. So we're going to take you behind the scenes. And this was meant to be a behind-the-scenes without professional audio. So I'm going to do a bit of a voiceover and then I'll let you hear some of the audio as we try to get to this shot. And it's not the greatest audio, but bear with me through that. But I'll take you through showing up on the scene and walking around and then you'll see how this looks as we set this up. So we basically had a speedlight, Canon 600 EXRT speedlight, with a blue gel in the cave. And then I had a zappelin. You'll see that in the shot. That was our key light. And so you'll see how it'll come together. Look at all that crappies carrying. All right, so here's a shot of Brittany and I walking down. You can see I have quite a bit of gear. I brought the 35 inch zappelin, a light stand. I also brought a beauty dish with me. I had a couple of think tank bags because I was shooting video as well. And then I also had my glide cam. So I had way too much gear. And it was about a 10, 15 minute walk from the parking lot down to this cave. Now this is an old British outpost where they used to store ammo. And sometimes I go on little adventures looking for interesting places to shoot. And I saw this and you can see there's the cave there. So I came up with the idea of using complimentary colors and using the blue gel to make it look like an ice cave.  So there's a bit of water and it was kind of a swampy area. But I thought what a cool location for a shoot. So you're gonna see some behind the scenes. We'll have some live audio. Like I said before, it's not the greatest standing audio. We just sort of meant to do it as behind the scenes and so the audio is not professional. So here I am shooting some video. This was for the YouTube intro for this behind the scenes. So I'm using the glide cam and I'm using my Canon 5D Mark III with the 24 to 70. And then I just had Brittany pretend she was in a modeling shoot. So she's going through some reposals. And what's great is she's got a lot of expressions. So it's fun if you can find someone that you can work with that's really good with the facial expressions. And then here I am, I'm setting up the Zeppelin and it takes a bit of work. There's a little bit of a speed ring and then you've got to put it all in. I think there's 16 different points you have to put in the speed ring. And then we're just using the internal baffle. We weren't using two layers of diffusion, just one and here's Josh climbing around. He can't lift it, he's not strong enough. He's not strong as me. And there he is, that's one of his great poses. And then here I am, I'm metering the light and we're shooting at f5.6. So first I'm metering this light and then I'm metering the speed light in the cave. Just trying to see how much light is bouncing back. And if I could do this again, I'd probably take the speed light down in the cave just a little bit, it's a little bit bright. You'll see some shots in a second. And that's pretty much it. So we're gonna switch to the live audio now and it's not the greatest quality, but anyway, just check out this. No pressure. No pressure. So I had to totally nail this or else. Yeah. So yeah, I'm gonna shoot at about 70 now. No lights for you. Come back next year. I'll just shoot from here and then I'll look at the light and see what it looks. I was trying to get higher, I just wanna get it. Okay, perfect, hold that actually. What's my turn again, Ronald? Hm? Okay, hold it. Let's go get those lights. Just gonna say, oh, hang on, that's cool. I almost have to get taller. Okay, right there, hold it. I might as well just go back and do full light. It's pretty cool. I almost got to shoot it from over here. Get that right position at that light. Oh, here we go, shoot it. We're getting it. Couple of strokes, brush strokes, keep moving too, as you move. So how about we do two? You get a pose, I'll take the picture then I'll see where I have to move and you keep that pose. Okay. That way I can nail that for blueish behind you. That's pretty good, actually. Hold that right there. Oh, cool, I like that. Hold that, let me just look. Oh, that's almost made me taller. Oh, you'll see that. Yeah, that's pretty cool. Hey, it's great to hear again. If you found this tip helpful, give me a thumbs up for this video and if you're not already a subscriber, just click on that subscribe button. And if you have any comments or tips that you think will help other photographers, you can leave them in the comments section below. All right, thanks for watching this video and I'll see you in the next one.